diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..b478bac1d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +Type of pull request: +- [ ] New plugin (Please include [new plugin checklist](https://github.com/hughsie/fwupd/wiki/New-plugin-checklist)) +- [ ] Code fix +- [ ] Feature +- [ ] Documentation diff --git a/.gitignore b/.gitignore index 2ffc0c825..d5a589422 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ /prime /stage /snap/.snapcraft +/libxmlb /*.deb /*.ddeb /*.changes diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/NEWS b/NEWS deleted file mode 100644 index 533dc2e34..000000000 --- a/NEWS +++ /dev/null @@ -1,893 +0,0 @@ -Version 1.1.4 -~~~~~~~~~~~~~ -Released: 2018-11-07 - -New Features: - - Stop any running daemon over dbus before loading engine (Mario Limonciello) - -Bugfixes: - - Use HTTPS_PROXY if set (Richard Hughes) - - Make the dell-dock plugin more robust in several ways (Mario Limonciello) - - Adjust EVB board handling (Mario Limonciello, RyanChang) - -Version 1.1.3 -~~~~~~~~~~~~~ -Released: 2018-10-12 - -New Features: - - Add a plugin for an upcoming Dell USB-C dock (Mario Limonciello) - - Add support for devices to show an estimated flash time (Mario Limonciello) - - Add support for Realtek USB devices using vendor HID and HUB commands (Richard Hughes) - - Allow firmware files to depend on versions from other devices (Richard Hughes) - -Bugfixes: - - Adjust panamera ESM update routine for some reported issues (Mario Limonciello) - - Check the amount of free space on the ESP before upgrading (Richard Hughes) - - Don't show devices pending a reboot in GetUpgrades (Mario Limonciello) - - Fix possible crash in the thunderbolt-power plugin (Richard Hughes) - - Make various parts of the daemon thread-safe (Richard Hughes) - - Redirect all debugging output to stderr instead of stdout (Mario Limonciello) - - Run the Dell plugin initialization after the UEFI plugin (Mario Limonciello) - - Stop showing the current release during updates in fwupdmgr (Mario Limonciello) - - Update all sub-devices for a composite update (Mario Limonciello) - -Version 1.1.2 -~~~~~~~~~~~~~ -Released: 2018-09-10 - -New Features: - - Add a new device flag "ignore-validation" that will override checks (Mario Limonciello) - - Add a new plugin to enumerate EC firmware (Richard Hughes) - - Add a new plugin to update NVMe hardware (Richard Hughes, Mario Limonciello) - - Add a plugin for updating using the flashrom command line tool (Richard Hughes) - - Allow the device list to take care of waiting for the device replug (Richard Hughes) - - Allow updating just one specific device from the command line (Richard Hughes) - - Allow upgrades using a self-signed fwupd.efi binary (Richard Hughes) - - Download firmware if the user specifies a URI (Richard Hughes) - - Include serial number in daemon device output when trusted (Mario Limonciello) - - Notify all plugins of device removals through a new vfunc (Mario Limonciello) - - Use boltd force power API if available (Mario Limonciello) - -Bugfixes: - - Add an install hook for classic snap (Mario Limonciello) - - Allow forcing installation even if no AC power is applied (Mario Limonciello) - - Allow using --force to ignore version_lowest (Mario Limonciello) - - Always use the same HardwareIDs as Windows (Richard Hughes) - - Check the device state before assuming a fake DFU runtime (Richard Hughes) - - Copy over parent GUIDs from other plugin donors (Mario Limonciello) - - Detect location of python3 interpreter (Mario Limonciello) - - Do not add udev devices after a small delay (Richard Hughes) - - Don't fail to run if compiled without GPG/PKCS7 (Salud Lemus, Mario Limonciello) - - Fix a segfault in fwupdtool caused by cleanup of USB plugins (Mario Limonciello) - - Implement the systemd recommendations for offline updates (Richard Hughes) - - Improve performance when reading keys from the quirk database (Richard Hughes) - - Remove children of devices when the parent is removed (Mario Limonciello) - - Rewrite synapticsmst to use modern error handling (Mario Limonciello) - - Rewrite the unifying plugin to use the new daemon-provided functionality (Richard Hughes) - - Show a time estimate on the progressbar after an update has started (Mario Limonciello) - -Version 1.1.1 -~~~~~~~~~~~~~ -Released: 2018-08-13 - -New Features: - - Add support for the Synaptics Panamera hardware (Mario Limonciello) - - Add validation for Alpine and Titan Ridge (Andrei Emeltchenko, Mika Westerberg) - - Improve the Redfish plugin to actually work with real hardware (Gary Lin) - -Bugfixes: - - Allow different plugins to add the same device (Richard Hughes) - - Allow flashing unifying devices in recovery mode (Richard Hughes) - - Allow running synapticsmst on non-Dell hardware (Mario Limonciello) - - Check the ESP for sanity at at startup (Richard Hughes, Mario Limonciello) - - Do not hold hidraw devices open forever (Richard Hughes) - - Don't override _FORTIFY_SOURCE when building the EFI binary (Richard Hughes) - - Don't show passwords in fwupdmgr (Richard Hughes, Mario Limonciello) - - Fix a potential segfault in smbios data parsing (Mario Limonciello) - - Fix encoding the GUID into the capsule EFI variable (Mario Limonciello) - - Fix various bugs when reading the thunderbolt version number (Mika Westerberg) - - Reboot synapticsmst devices at the end of flash cycle (Mario Limonciello) - - Show status messages when the daemon is initializing (Mario Limonciello) - - Show the correct title when updating devices (Richard Hughes, Mario Limonciello) - - Show the reasons that plugins are not run on the CLI (Mario Limonciello) - - Use localedir in po/make-images (Jan Tojnar) - -Version 1.1.0 -~~~~~~~~~~~~~ -Released: 2018-07-11 - -New Features: - - Add a initial Redfish support (Richard Hughes) - - Add a tool to mimic the original fwupdate CLI interface (Richard Hughes) - - Allow devices to assign a plugin from the quirk subsystem (Richard Hughes) - - Change the quirk file structure to be more efficient (Richard Hughes) - - Merge fwupdate functionality into fwupd (Richard Hughes, Mario Limonciello) - - Run a plugin vfunc before and after all the composite devices are updated (Richard Hughes) - - Support more Wacom tablets (Richard Hughes) - -Bugfixes: - - Add release information for locked devices (Richard Hughes) - - Allow building with older meson (Mario Limonciello) - - Detect the EFI system partition location at runtime (Mario Limonciello) - - Do not use 8bitdo bootloader commands after a successful flash (Richard Hughes) - - Enable accesing downloaded files in flatpak and snap (Mario Limonciello) - - Fix a potential buffer overflow when applying a DFU patch (Richard Hughes) - - Fix downgrading older releases to devices (Richard Hughes) - - Fix flashing devices that require a manual replug (Richard Hughes) - - Fix several small memory leaks in various places (Richard Hughes) - - Fix the retrieval of Redfish version (Gary Lin) - - Fix unifying failure to detach when using a slow host controller (Richard Hughes) - - Set the Wacom device status when erasing and writing firmware (Richard Hughes) - - Show errors in the CLI if unable to access directory (Mario Limonciello) - - Use the parent device name for Wacom sub-modules (Richard Hughes) - -Version 1.0.8 -~~~~~~~~~~~~~ -Released: 2018-06-07 - -New Features: - - Add an plugin to update some future Wacom tablets (Richard Hughes) - - Add 'fwupdmgr get-topology' to show logical device tree (Richard Hughes, Mario Limonciello) - - Add support for creating a flatpak (Richard Hughes) - - Add support for creating a snap (Mario Limonciello, Richard Hughes) - - Add support for Motorola S-record files (Richard Hughes) - - Add the Linux Foundation public GPG keys for firmware and metadata (Richard Hughes) - - Show a translated warning when the server is limiting downloads (Richard Hughes) - -Bugfixes: - - Add a firmware diagnostic tool called fwupdtool (Richard Hughes, Mario Limonciello) - - Adjust all licensing to LGPL 2.1+ (Mario Limonciello) - - Allow installing more than one firmware using 'fwupdmgr install' (Richard Hughes) - - Allow specifying hwids with OR relationships (Richard Hughes) - - Do not call fu_plugin_init() on blacklisted plugins (Richard Hughes) - - Do not require libcolorhug to build (Richard Hughes) - - Fix a crash in libfwupd where no device ID is set (Richard Hughes) - - Fix a potential DoS in libdfu by limiting holes to 1MiB (Richard Hughes) - - Fix a segfault that sometimes occurs during cleanup of USB plugins (Mario Limonciello) - - Fix Hardware-ID{0,1,2,12} compatibility with Microsoft (Gergely Risko) - - Hide devices that aren't updatable by default in fwupdmgr (Mario Limonciello) - - Search all UEFI GUIDs when matching hardware (Richard Hughes, Mario Limonciello) - - Stop matching Nintendo Switch Pro in the 8bitdo plugin (Mario Limonciello) - -Version 1.0.7 -~~~~~~~~~~~~~ -Released: 2018-04-30 - -New Features: - - Add enable-remote and disable-remote commands to fwupdmgr (Richard Hughes) - - Add fu_plugin_add_compile_version() for libraries to use (Richard Hughes) - - Allow requiring specific versions of libraries for firmware updates (Richard Hughes) - - If no remotes are enabled try to enable the LVFS (Mario Limonciello) - - Show a warning with interactive prompt when enabling a remote (Richard Hughes) - -Bugfixes: - - Check that EFI system partition is mounted before update (Mario Limonciello) - - Disable synapticsmst remote control on failure (Sjoerd Simons) - - Don't recoldplug thunderbolt to fix a flashing failure (Mario Limonciello) - - Fix SQL error when running 'fwupdmgr clear-offline' (Richard Hughes) - - Improve the update report message (Mario Limonciello) - - Only enumerate Dell Docks if the type is known (Sjoerd Simons) - - Only run certtool if a new enough gnutls is present (Mario Limonciello) - - Prevent a client crash if the daemon somehow sends invalid data (Richard Hughes) - - Reboot after scheduling using logind not systemd (Richard Hughes) - - Use the right encoding for the label in make-images (Niels Ole Salscheider) - -Version 1.0.6 -~~~~~~~~~~~~~ -Released: 2018-03-12 - -New Features: - - Add bash completion for fwupdmgr (Mario Limonciello) - - Add support for newest Thunderbolt chips (Andrei Emeltchenko) - - Allow all functions that take device arguments to be prompted (Mario Limonciello) - - Allow devices to use the runtime version when in bootloader mode (Richard Hughes) - - Allow overriding ESP mount point via conf file (Mario Limonciello) - - Delete any old fwupdate capsules and efivars when launching fwupd (Richard Hughes) - - Generate Vala bindings (Robert Ancell) - -Bugfixes: - - Allow ctrl-d out of the prompt for devices (Mario Limonciello) - - Allow to create package out of provided binary (Andrei Emeltchenko) - - Correct handling of unknown Thunderbolt devices (Yehezkel Bernat) - - Correctly detect new remotes that are manually copied (Richard Hughes) - - Fix a crash related to when passing device to downgrade in CLI (Mario Limonciello) - - Fix running the self tests when no fwupd is installed (Richard Hughes) - - Fix Unifying signature writing and parsing for Texas bootloader (Ogier Bouvier) - - Only send success and failure reports to the server (Richard Hughes) - - Use a CNAME to redirect to the correct CDN for metadata (Richard Hughes) - - Use a longer timeout when powering back the Thunderbolt device (Richard Hughes) - -Version 1.0.5 -~~~~~~~~~~~~~ -Released: 2018-02-14 - -New Features: - - Offer to reboot when processing an offline update (Richard Hughes) - - Report the efivar, libsmbios and fwupdate library versions (Mario Limonciello) - - Report Thunderbolt safe mode and SecureBoot status (Mario Limonciello) - - Show the user a URL when they report a known problem (Richard Hughes) - - Support split cabinet archives as produced by Windows Update (Richard Hughes) - -Bugfixes: - - Be more careful deleting and modifying device history (Richard Hughes) - - Clarify which devices don't have upgrades (Mario Limonciello) - - Ensure the Thunderbolt version is xx.yy (Richard Hughes) - - Fix a daemon warning when using fwupdmgr get-results (Richard Hughes) - - Fix crasher with MST flashing (Mario Limonciello) - - Fix DFU detach with newer releases of libusb (Richard Hughes) - - Include the device VID and PID when generating the device-id (Richard Hughes) - - Set the RemoteId when using GetDetails (Richard Hughes) - - Stop matching 8bitdo DS4 controller VID/PID (Mario Limonciello) - - Use help2man for dfu-tool and drop docbook dependencies (Mario Limonciello) - - Use ngettext for any strings with plurals (Piotr Drąg) - - Use the default value if ArchiveSizeMax is unspecified (Richard Hughes) - -Version 1.0.4 -~~~~~~~~~~~~~ -Released: 2018-01-25 - -New Features: - - Add D-Bus methods to get and modify the history information (Richard Hughes) - - Allow the user to share firmware update success or failure (Richard Hughes) - - Ask the user to refresh metadata when it is very old (Richard Hughes) - - Store firmware update success and failure to a local database (Richard Hughes) - -Bugfixes: - - Add a device name for locked UEFI devices (Mario Limonciello) - - Allow each plugin to opt-in to the recoldplug action (Richard Hughes) - - Fix firmware downloading using gnome-software (Richard Hughes) - - Fix UX capsule reference to the one specified in efivar (Mario Limonciello) - - Never add two devices to the daemon with the same ID (Richard Hughes) - - Rescan supported flags when refreshing metadata (Richard Hughes) - -Version 1.0.3 -~~~~~~~~~~~~~ -Released: 2018-01-09 - -New Features: - - Add a new plugin to add support for CSR "Driverless DFU" (Richard Hughes) - - Add initial SF30/SN30 Pro support (Mario Limonciello) - - Support AppStream metadata with relative URLs (Richard Hughes) - -Bugfixes: - - Add more metadata to the user-agent string (Richard Hughes) - - Block owned Dell TPM updates (Mario Limonciello) - - Choose the correct component from provides matches using requirements (Richard Hughes) - - Do not try to parse huge compressed archive files (Richard Hughes) - - Fix a double-free bug in the Udev code (Philip Withnall) - - Handle Thunderbolt "native" mode (Yehezkel Bernat) - - Use the new functionality in libgcab >= 1.0 to avoid writing temp files (Richard Hughes) - -Version 1.0.2 -~~~~~~~~~~~~~ -Released: 2017-11-28 - -New Features: - - Add a plugin for the Nitrokey Storage device (Richard Hughes) - - Add support for the original AVR DFU protocol (Richard Hughes) - - Allow different plugins to claim the same device (Richard Hughes) - - Allow quirks to set common USB properties (Richard Hughes) - - Move a common plugin functionality out to a new shared object (Richard Hughes) - - Optionally delay the device removal for better replugging (Richard Hughes) - - Set environment variables to allow easy per-plugin debugging (Richard Hughes) - - Use a SHA1 hash for the internal DeviceID (Richard Hughes) - -Bugfixes: - - Add quirk for AT32UC3B1256 as used in the RubberDucky (Richard Hughes) - - Disable the dell plugin if libsmbios fails (Mario Limonciello) - - Don't register for USB UDev events to later ignore them (Richard Hughes) - - Fix a possible buffer overflow when debugging ebitdo devices (Richard Hughes) - - Fix critical warning when more than one remote fails to load (Richard Hughes) - - Fix DFU attaching AVR32 devices like the XMEGA (Richard Hughes) - - Ignore useless Thunderbolt device types (Mario Limonciello) - - Refactor ColorHug into a much more modern plugin (Richard Hughes) - - Release the Steelseries interface if getting the version failed (Richard Hughes) - - Remove autoconf-isms from the meson configure options (Richard Hughes) - - Show a nicer error message if the requirement fails (Richard Hughes) - - Sort the output of GetUpgrades correctly (Richard Hughes) - -Version 1.0.1 -~~~~~~~~~~~~~ -Released: 2017-11-09 - -New Features: - - Add support for HWID requirements (Richard Hughes) - - Add support for programming various AVR32 and XMEGA parts using DFU (Richard Hughes) - - Add the various DFU quirks for the Jabra Speak devices (Richard Hughes) - - Allow specifying the output file type for 'dfu-tool read' (Richard Hughes) - - Move the database of supported devices out into runtime loaded files (Richard Hughes) - - Support the IHEX record type 0x05 (Richard Hughes) - - Use help2man to generate the man page at build time (Richard Hughes) - - Use the new quirk infrastructure for version numbers (Richard Hughes) - -Bugfixes: - - Catch invalid Dell dock component requests (Mario Limonciello) - - Correctly output Intel HEX files with > 16bit offset addresses (Richard Hughes) - - Do not try to verify the element write if upload is unsupported (Richard Hughes) - - Fix a double-unref when updating any 8Bitdo device (Richard Hughes) - - Fix crash when enumerating with Dell dock connected but with no UEFI (Mario Limonciello) - - Fix uploading large firmware files over DFU (Richard Hughes) - - Format the BCD USB revision numbers correctly (Richard Hughes) - - Guess the DFU transfer size if it is not specified (Richard Hughes) - - Include the reset timeout as wValue to fix some DFU bootloaders (Richard Hughes) - - Make the error message clearer when sans fonts are missing (Mario Limonciello) - - Support devices with truncated DFU interface data (Richard Hughes) - - Use the correct remote-specified username and passord when using fwupdmgr (Richard Hughes) - - Use the correct wDetachTimeOut when writing DFU firmware (Richard Hughes) - - Verify devices with legacy VIDs are actually 8Bitdo controllers (Richard Hughes) - -Version 1.0.0 -~~~~~~~~~~~~~ -Released: 2017-10-09 - -Notes: - - This release breaks API and ABI to remove deprecated symbols - - libdfu is now not installed as a shared library - -New Features: - - Add a human-readable title for each remote (Richard Hughes) - - Add a method to return a list of upgrades for a specific device (Richard Hughes) - - Add an 'Summary' and 'Icons' properties to each device (Richard Hughes) - - Add FuDeviceLocker to simplify device open/close lifecycles (Richard Hughes) - - Add functionality to blacklist Dell HW with problems (Mario Limonciello) - - Add fu_plugin_check_supported() (Richard Hughes) - - Add fwupd_remote_get_checksum() to use in client programs (Richard Hughes) - - Add ModifyRemote as an easy way to enable and disable remotes (Richard Hughes) - - Add the plugin documentation to the main gtk-doc (Richard Hughes) - - Allow plugins to depend on each other (Richard Hughes) - - Disable the fallback USB plugin (Richard Hughes) - - Parse the SMBIOS v2 and v3 DMI tables directly (Richard Hughes) - - Support uploading the UEFI firmware splash image (Richard Hughes) - - Use the intel-wmi-thunderbolt kernel module to force power (Mario Limonciello) - -Bugfixes: - - Only run SMI to toggle host MST GPIO on Dell systems with host MST (Mario Limonciello) - - Disable unifying support if no CONFIG_HIDRAW support (Richard Hughes) - - Do not auto-open all USB devices at startup (Richard Hughes) - - Do not fail to load the daemon if cached metadata is invalid (Richard Hughes) - - Do not use system-specific infomation for UEFI PCI devices (Richard Hughes) - - Fix a crash when using fu_plugin_device_add_delay() (Richard Hughes) - - Fix the libdfu self test failure on s390 and ppc64 (Richard Hughes) - - Fix various printing issues with the progressbar (Richard Hughes) - - Generate the LD script from the GObject introspection data (Richard Hughes) - - Never fallback to an offline update from client code (Richard Hughes) - - Only set the Dell coldplug delay when we know we need it (Mario Limonciello) - - Prefer to use HWIDs to get DMI keys and DE table (Mario Limonciello) - -Version 0.9.7 -~~~~~~~~~~~~~ -Released: 2017-09-01 - -New Features: - - Add a configure switch for the LVFS remotes (Richard Hughes) - - Add a FirmwareBaseURI parameter to the remote config (Richard Hughes) - - Add a firmware builder that uses bubblewrap (Richard Hughes) - - Add a python script to create fwupd compatible cab files from Microsoft .exe files (Max Ehrlich) - - Add a thunderbolt plugin for new kernel interface (Christian Kellner, Yehezkel Bernat) - - Allow plugins to get DMI data from the hardware in a safe way (Richard Hughes) - - Allow plugins to set metadata on devices created by other plugins (Richard Hughes, Mario Limonciello) - - Optionally install the LVFS PKCS7 root certificate (Richard Hughes) - - Optionally use GnuTLS to verify PKCS7 certificates (Richard Hughes) - -Bugfixes: - - Add back options for HAVE_SYNAPTICS and HAVE_THUNDERBOLT (Mario Limonciello) - - Allow configuring systemd and udev directories (Mario Limonciello) - - Enable C99 support in meson.build (Philip Withnall) - - Fix an incomplete cipher when using XTEA on data not in 4 byte chunks (Richard Hughes) - - Fix minor const-correctness issues (Philip Withnall) - - Implement thunderbolt image validation (Yehezkel Bernat, Christian Kellner) - - Remove the confusing ALLOW_OFFLINE and ALLOW_ONLINE flags (Richard Hughes) - - Show a bouncing progress bar if the percentage remains at zero (Richard Hughes) - - Use a hwid to match supported systems for synapticsmst (Mario Limonciello) - - Use the new bootloader PIDs for Unifying pico receivers (Richard Hughes) - - When thunderbolt is in safe mode on a Dell recover using SMBIOS (Mario Limonciello) - -Version 0.9.6 -~~~~~~~~~~~~~ -Released: 2017-08-03 - -New Features: - - Add DfuPatch to support forward-only firmware patching (Richard Hughes) - - Add --version option to fwupdmgr (Richard Hughes, Mario Limonciello) - - Display all errors recorded by efi_error tracing (Mario Limonciello) - - Make building introspection optional (Patrick Ohly) - - Support embedded devices with local firmware metadata (Richard Hughes) - -Bugfixes: - - Check all the device GUIDs against the blacklist when added (Richard Hughes) - - Correct a memory leak in Dell plugin (Mario Limonciello, Richard Hughes) - - Default to "en" for UEFI capsule graphics (Mario Limonciello) - - Don't log a warning when an unknown unifying report is parsed (Richard Hughes) - - Enable test suite via /etc/fwupd.conf (Mario Limonciello) - - Fix a hang on 32 bit computers (Richard Hughes) - - Fix compilation of the policy on a variety of configurations (Mario Limonciello) - - Fix UEFI crash when the product name is NULL (Richard Hughes) - - Make flashing ebitdo devices work with fu-ebitdo-tool (Chris Lee) - - Make messages from installing capsules useful (Mario Limonciello) - - Make sure the unifying percentage completion goes from 0% to 100% (Richard Hughes) - - Run the plugin coldplug methods in a predictable order (Richard Hughes) - - Test UEFI for kernel support during coldplug (Mario Limonciello) - - Use new GUsb functionality to fix flashing Unifying devices (Richard Hughes) - -Version 0.9.5 -~~~~~~~~~~~~~ -Released: 2017-07-04 - -New Features: - - Add a get-remotes command to fwupdmgr (Richard Hughes) - - Add a plugin to get the version of the AMT ME interface (Richard Hughes) - - Add Arch Linux to CI (Bruno Pagani) - - Add some installed tests flashing actual hardware (Richard Hughes) - - Allow flashing Unifying devices in bootloader modes (Richard Hughes) - - Allow ordering the metadata remotes (Richard Hughes) - -Bugfixes: - - Do not check the runtime if the DFU device is in bootloader mode (Richard Hughes) - - Do not unlock devices when doing VerifyUpdate (Richard Hughes) - - Filter by Unifying SwId when making HID++2.0 requests (Richard Hughes) - - Fix downgrades when version_lowest is set (Richard Hughes) - - Fix the self tests when running on PPC64 big endian (Richard Hughes) - - Move the remotes parsing from the client to the server (Richard Hughes) - - Split up the Unifying HID++2.0 and HID++1.0 functionality (Richard Hughes) - - Store the metadata files rather than merging to one store (Richard Hughes) - - Use a longer timeout for some Unifying operations (Richard Hughes) - - Use the UFY DeviceID prefix for Unifying devides (Richard Hughes) - -Version 0.9.4 -~~~~~~~~~~~~~ -Released: 2017-06-15 - -New Features: - - Add installed tests that use the daemon (Richard Hughes) - - Add the ability to restrict firmware to specific vendors (Richard Hughes) - - Enable Travis CI for Fedora and Debian (Richard Hughes, Mario Limonciello) - - Export some more API for dealing with checksums (Richard Hughes) - - Generate a images for status messages during system firmware update (Peter Jones) - - Show progress download when refreshing metadata (Richard Hughes) - -Bugfixes: - - Compile with newer versions of meson (Richard Hughes, Mario Limonciello) - - Ensure that firmware provides are legal GUIDs (Richard Hughes) - - Fix a common crash when refreshing metadata (Richard Hughes) - - Use the correct type signature in the D-Bus introspection file (Richard Hughes) - -Version 0.9.3 -~~~~~~~~~~~~~ -Released: 2017-06-07 - -New Features: - - Add a 'downgrade' command to fwupdmgr (Richard Hughes) - - Add a 'get-releases' command to fwupdmgr (Richard Hughes) - - Add support for ConsoleKit2 (Eric Koegel) - - Add support for Microsoft HardwareIDs (Richard Hughes) - - Allow downloading metadata from more than just the LVFS (Richard Hughes) - - Allow multiple checksums on devices and releases (Richard Hughes) - -Bugfixes: - - Allow to specify bindir (Timo Gurr) - - Correctly open Unifying devices with original factory firmware (Richard Hughes) - - Deprecate some of the old FwupdResult API (Richard Hughes) - - Do not copy the origin from the new metadata file (Richard Hughes) - - Do not expect a Unifying reply when issuing a REBOOT command (Richard Hughes) - - Do not re-download firmware that exists in the cache (Richard Hughes) - - Fix a problem when testing for a Dell system (Mario Limonciello) - - Fix flashing new firmware to 8bitdo controllers (Richard Hughes) - - Increase minimum required AppStream-Glib version to 0.6.13 (Chris Mayo) - - Make documentation and man pages optional (Chris Mayo) - - Make systemd dependency at least version 231 (Mario Limonciello) - - Only decompress the firmware after the signature check (Richard Hughes) - - Remove 'lib' prefix when looking for libraries (Mirco Tischler) - - Return the remote ID when getting updates about hardware (Richard Hughes) - - Send the daemon the remote ID when sending firmware metadata (Richard Hughes) - -Version 0.9.2 -~~~~~~~~~~~~~ -Released: 2017-05-22 - -New Features: - - Add support for Unifying DFU features (Richard Hughes) - -Bugfixes: - - Do not spew a critial warning when parsing an invalid URI (Richard Hughes) - - Ensure device is closed if did not complete setup (Richard Hughes) - - Ensure steelseries device is closed if it returns an invalid packet (Richard Hughes) - - Fix man page installation location (Mario Limonciello) - - Ignore spaces in the Unifying version prefix (Richard Hughes) - - Set HAVE_POLKIT_0_114 when polkit is newer than 0.114 (Moritz Kiefer) - -Version 0.9.1 -~~~~~~~~~~~~~ -Released: 2017-04-28 - -New Features: - - Add a config option to allow runtime disabling plugins by name (Richard Hughes) - - Add the Meson build system and remove autotools (Richard Hughes) - - Support signed Intel HEX files (Richard Hughes) - -Bugfixes: - - Add DFU quirk for OpenPICC and SIMtrace (Richard Hughes) - - Create directories in /var/cache as required (Richard Hughes) - - Refactor the unifying plugin now we know more about the hardware (Richard Hughes) - - Set the source origin when saving metadata (Richard Hughes) - - Support proxy servers in fwupdmgr (Richard Hughes) - - Use a 60 second timeout on all client downloads (Richard Hughes) - -Version 0.8.1 -~~~~~~~~~~~~~ -Released: 2017-02-27 - -Bugfixes: - - Adjust systemd confinement restrictions (Mario Limonciello, Richard Hughes) - - Do not hardcode docbook2man path (Kai Krakow) - - Don't initialize libsmbios on unsupported systems (Mario Limonciello) - - Fix a crash when enumerating devices on a Dell WLD15 (Richard Hughes) - - Fix compiler warnings (Kai Krakow) - - Fix fwupdmgr timeout with missing pending database (Richard Hughes) - -Version 0.8.0 -~~~~~~~~~~~~~ -Released: 2017-02-08 - -New Features: - - Add a set of vfuncs that are run before and after a device update (Richard Hughes) - - Add Dell-specific functionality to allow other plugins turn on TBT/GPIO (Mario Limonciello) - - Add support for Intel Thunderbolt devices (Richard Hughes, Mario Limonciello) - - Add support for Logitech Unifying devices (Richard Hughes) - - Add support for Synaptics MST cascades hubs (Mario Limonciello) - - Add support for the Altus-Metrum ChaosKey device (Richard Hughes) - - Add VerifyUpdate to update the device checksums server-side (Richard Hughes) - - Allow the metadata to match a version of fwupd and the existing fw version (Richard Hughes) - -Bugfixes: - - Add a new method for forcing a controller to flash mode (Mario Limonciello) - - Always make sure we're getting a C99 compiler (Richard Hughes) - - Close USB devices before error returns (Tsunghan Liu) - - Don't read data from some DfuSe targets (Richard Hughes) - - Include all debug messages when run with --verbose (Richard Hughes) - - Return the pending UEFI update when not on AC power (Richard Hughes) - - Use a heuristic for the start address if the firmware has no DfuSe footer (Richard Hughes) - - Use more restrictive settings when running under systemd (Richard Hughes, Mario Limonciello) - -Version 0.7.5 -~~~~~~~~~~~~~ -Released: 2016-10-19 - -New Features: - - Add a 'replace-data' command to dfu-tool (Richard Hughes) - - Use an animated progress bar when performing DFU operations (Richard Hughes) - -Bugfixes: - - Add quirks for HydraBus as it does not have a DFU runtime (Richard Hughes) - - Don't create the UEFI dummy device if the unlock will happen on next boot (Richard Hughes) - - Enable hardening flags on more binaries (Mario Limonciello) - - Fix an assert when unlocking the dummy ESRT device (Richard Hughes) - - Fix writing firmware to devices using the ST reference bootloader (Richard Hughes) - - Match the Dell TB16 device (Mario Limonciello) - - Re-get the quirks when the DfuDevice gets a new GUsbDevice (Richard Hughes) - - Show the nicely formatted target name for DfuSe devices (Richard Hughes) - - Verify devices support updating in mode they are called (Mario Limonciello) - -Version 0.7.4 -~~~~~~~~~~~~~ -Released: 2016-09-19 - -New Features: - - Add dfu_firmware_add_symbol() (Richard Hughes) - - Allow the argument to 'dfu-tool set-release' be major.minor (Richard Hughes) - - Load the Altos USB descriptor from ELF files (Richard Hughes) - - Support writing the IHEX symbol table (Richard Hughes) - -Bugfixes: - - Add a fallback for older appstream-glib releases (Richard Hughes) - - Fix a possible crash when uploading firmware files using libdfu (Richard Hughes) - - Fix libfwupd self tests when a host-provided fwupd is not available (Richard Hughes) - - Show the human-readable version in the 'dfu-tool dump' output (Richard Hughes) - - Write the ELF files with the correct section type (Richard Hughes) - -Version 0.7.3 -~~~~~~~~~~~~~ -Released: 2016-08-29 - -New Features: - - Add a set-address and set-target-size commands to dfu-util (Richard Hughes) - - Add a small library for talking with 0bitdo hardware (Richard Hughes) - - Add Dell TPM and TB15/WD15 support via new Dell provider (Mario Limonciello) - - Add FU_DEVICE_FLAG_NEEDS_BOOTLOADER (Richard Hughes) - - Add fwupd_client_get_status() (Richard Hughes) - - Add fwupd_result_get_unique_id() (Richard Hughes) - - Add initial ELF reading and writing support to libdfu (Richard Hughes) - - Add support for installing multiple devices from a CAB file (Richard Hughes) - - Allow providers to export percentage completion (Richard Hughes) - - Show a progress notification when installing firmware (Richard Hughes) - - Show the vendor flashing instructions when installing (Richard Hughes) - -Bugfixes: - - Add XPS 9250 to Dell TPM modeswitch blacklist (Mario Limonciello) - - Allow blacklisting devices by their GUID (Richard Hughes) - - Conditionally enable all providers based upon installed (Mario Limonciello) - - Display flashes left in results output when it gets low (Mario Limonciello) - - Do not attempt to add DFU devices not in runtime mode (Richard Hughes) - - Do not use the deprecated GNOME_COMPILE_WARNINGS (Richard Hughes) - - Don't fail while checking versions or locked state (Richard Hughes) - - Embed fwupd version in generated documentation (Mario Limonciello) - - Ensure the ID is set when getting local firmware details (Richard Hughes) - - Fix gtk-doc build when srcdir != builddir (Ting-Wei Lan) - - Fix libdfu hang when parsing corrupt IHEX files (Richard Hughes) - - Ignore devices that do not add at least one GUID (Richard Hughes) - - In get-details output, display the blob filename (Mario Limonciello) - - Save the unique ID in the pending database (Richard Hughes) - - Support the 'DEVO' cipher kind in libdfu (Richard Hughes) - - Switch to the Amazon S3 CDN for firmware metadata (Richard Hughes) - - Update fwupdmgr manpage for new commands and arguments (Mario Limonciello) - - Use a private gnupg key store (Richard Hughes) - - Use the correct firmware when installing a composite device (Richard Hughes) - - Use the SHA1 hash of the local file data as the origin (Richard Hughes) - -Version 0.7.2 -~~~~~~~~~~~~~ -Released: 2016-06-13 - -New Features: - - Add a GetDetailsLocal() method to eventually replace GetDetails() (Richard Hughes) - - Add fu_device_get_alternate() (Richard Hughes) - - Allow devices to have multiple assigned GUIDs (Richard Hughes) - - Allow metainfo files to match only specific revisions of devices (Richard Hughes) - - Show the DFU protocol version in 'dfu-tool list' (Richard Hughes) - -Bugfixes: - - Enforce allowing providers to take away flash abilities (Mario Limonciello) - - Only claim the DFU interface when required (Richard Hughes) - - Only return updatable devices from GetDevices() (Richard Hughes) - -Version 0.7.1 -~~~~~~~~~~~~~ -Released: 2016-05-13 - -New Features: - - Add a --force flag to override provider warnings (Mario Limonciello) - - Add device-added, device-removed and device-changed signals (Richard Hughes) - - Add dfu_image_get_element_default() (Richard Hughes) - - Add for a new device field "Flashes Left" (Mario Limonciello) - - Add fwupd_client_connect() (Richard Hughes) - - Add the 'monitor' debugging command for fwupdmgr (Richard Hughes) - - Add the 'supported' flag to the FuDevice (Richard Hughes) - -Bugfixes: - - Add summary and name field for Rival SteelSeries (Mario Limonciello) - - Fix a critical warning when restarting the daemon (Richard Hughes) - - Fix BE issues when reading and writing DFU files (Mario Limonciello, Richard Hughes) - - Make the device display name nicer (Richard Hughes, Richard Hughes) - - Match the AppStream metadata after a device has been added (Richard Hughes) - - Remove non-interactive pinentry setting from fu-keyring (Mario Limonciello) - - Return all update descriptions newer than the installed version (Richard Hughes) - - Set the device description when parsing local firmware files (Richard Hughes) - -Version 0.7.0 -~~~~~~~~~~~~~ -Released: 2016-04-01 - -New Features: - - Add a version plugin for SteelSeries hardware (Richard Hughes) - - Add FwupdClient and FwupdResult to libfwupd (Richard Hughes) - - Generate gtk-doc documentation for libfwupd (Richard Hughes) - - Return the device flags when getting firmware details (Richard Hughes) - - Support other checksum kinds (Richard Hughes) - -Bugfixes: - - Add Alienware to the version quirk table (Mario Limonciello) - - Allow the test suite to run in %check (Richard Hughes) - - Do not return updates that require AC when on battery (Richard Hughes) - - Do not use /tmp for downloaded files (Richard Hughes) - - Test that GPG key import actually was successful (Mario Limonciello) - -Version 0.6.3 -~~~~~~~~~~~~~ -Released: 2016-03-14 - -New Features: - - Add an unlock method for devices (Richard Hughes) - - Add a simple plugin infrastructure (Richard Hughes) - - Add ESRT enable method into UEFI provider (Mario Limonciello) - - Install the hardcoded firmware AppStream file (Richard Hughes) - -Bugfixes: - - Correct the BCD version number for DFU 1.1 (Richard Hughes) - - Do not use deprecated API from libappstream-glib (Richard Hughes) - - Ignore the DFU runtime on the DW1820A (Richard Hughes) - - Only read PCI OptionROM firmware when devices are manually unlocked (Richard Hughes) - - Require AC power before scheduling some types of firmware update (Richard Hughes) - - Show ignored DFU devices in dfu-util, but not in fwupd (Richard Hughes) - -Version 0.6.2 -~~~~~~~~~~~~~ -Released: 2016-02-12 - -New Features: - - Add 'Created' and 'Modified' properties on managed devices (Richard Hughes) - -Bugfixes: - - Fix get-results for UEFI provider (Mario Limonciello) - - Support vendor-specific UEFI version encodings (Richard Hughes) - -Version 0.6.1 -~~~~~~~~~~~~~ -Released: 2016-01-19 - -Bugfixes: - - Always persist ColorHug devices after replug (Richard Hughes) - - Do not misdetect different ColorHug devices (Richard Hughes) - - Only dump the profiling data when run with --verbose (Richard Hughes) - -Version 0.6.0 -~~~~~~~~~~~~~ -Released: 2015-12-07 - -Notes: - - This release adds a new GObject library called libdfu and a command line - client called dfu-tool. This is a low-level tool used to upgrade USB device - firmware and can either be shipped in the same package as fwupd or split off - as separate subpackages. - -New Features: - - Add support for automatically updating USB DFU-capable devices (Richard Hughes) - -Bugfixes: - - Emit the changed signal after doing an update (Richard Hughes) - - Export the AppStream ID when returning device results (Richard Hughes) - - Fix compile with --disable-shared (Richard Hughes) - - Use new API available in fwup 0.5 (Richard Hughes, Mario Limonciello) - - Use the same device identification string format as Microsoft (Richard Hughes) - -Version 0.5.3 -~~~~~~~~~~~~~ -Released: 2015-11-05 - -Bugfixes: - - Avoid seeking when reading the file magic during refresh (Richard Hughes) - - Do not assume that the compressed XML data will be NUL terminated (Richard Hughes) - - Use the correct user agent string for fwupdmgr (Richard Hughes) - -Version 0.5.2 -~~~~~~~~~~~~~ -Released: 2015-10-28 - -New Features: - - Add profiling data to debug slow startup times (Richard Hughes) - - Support cabinet archives files with more than one firmware (Richard Hughes) - -Bugfixes: - - Add the update description to the GetDetails results (Richard Hughes) - - Clear the in-memory firmware store only after parsing a valid XML file (Richard Hughes) - - Ensure D-Bus remote errors are registered at fwupdmgr startup (Richard Hughes) - - Fix verify-update to produce components with the correct provide values (Richard Hughes) - - Require appstream-glib 0.5.1 (Mirco Tischler) - - Show the dotted-decimal representation of the UEFI version number (Richard Hughes) - - When the version is from the 'FW' extension do not cache the device (Richard Hughes) - -Version 0.5.1 -~~~~~~~~~~~~~ -Released: 2015-09-21 - -Bugfixes: - - Fix the error message when no devices can be updated (Richard Hughes) - - Fix reading symlink to prevent crash with some compilers (Kalev Lember) - -Version 0.5.0 -~~~~~~~~~~~~~ -Released: 2015-09-15 - -New Features: - - Raise the dep on GLib to support and use g_autoptr() (Richard Hughes) - -Bugfixes: - - Do not merge existing firmware metadata (Richard Hughes) - - Do not reboot if racing with the PackageKit offline update mechanism (Richard Hughes) - -Version 0.1.6 -~~~~~~~~~~~~~ -Released: 2015-09-10 - -New Features: - - Remove fwsignd, we have the LVFS now (Richard Hughes) - -Bugfixes: - - Add application metadata when getting the updates list (Richard Hughes) - - Depend on appstream-glib >= 0.5.0 (Richard Hughes) - - Don't apply firmware if something else is processing the update (Richard Hughes) - - Install fwupd into /usr/lib/$(triplet)/fwupd instead (Mario Limonciello) - - Simplify the version properties on devices to avoid complexity (Richard Hughes) - - Update the offline update service to invoke right command (Kalev Lember) - - Use the new secure metadata URI (Richard Hughes) - -Version 0.1.5 -~~~~~~~~~~~~~ -Released: 2015-08-12 - -Notes: - - For the device verification code to work correctly you need at least - libappstream-glib 0.5.0 installed. - -New Features: - - Add a Raspberry Pi firmware provider (Richard Hughes) - - Add a simple config file to store the correct LVFS download URI (Richard Hughes) - - Make parsing the option ROM runtime optional (Richard Hughes) - -Bugfixes: - - Allow fwupd to be autostarted by systemd (Richard Hughes) - - Allow no arguments to 'fwupdmgr verify-update' and use sane defaults (Richard Hughes) - - Devices with option ROM are always internal (Richard Hughes) - - Do not pre-convert the update description from AppStream XML (Richard Hughes) - - Fix validation of written firmware (Richard Hughes) - - Move the verification and metadata matching phase to the daemon (Richard Hughes) - - Sign the test binary with the correct key (Richard Hughes) - - Use the AppStream 0.9 firmware specification by default (Richard Hughes) - -Version 0.1.4 -~~~~~~~~~~~~~ -Released: 2015-07-25 - -Notes: - - In this release we've moved the LVFS website to the fwupd project and made - them work really well together. To update all the firmware on your system - is now just a case of "fwupdmgr refresh && fwupdmgr update" - - We've also added verification of BIOS and PCI ROM firmware, which may be - useful for forensics or to verify that system updates have been applied. - -New Features: - - Actually parse the complete PCI option ROM (Richard Hughes) - - Add a 'fwupdmgr update' command to update all devices to latest versions (Richard Hughes) - - Add a simple signing server that operates on .cab files (Richard Hughes) - - Add a 'verify' command that verifies the cryptographic hash of device firmware (Richard Hughes) - - Allow clients to add new firmware metadata to the system cache (Richard Hughes) - - Move GetUpdates to the daemon (Richard Hughes) - - Move the LVFS website to the fwupd project (Richard Hughes) - -Bugfixes: - - Accept multiple files at one time when using fwupdmgr dump-rom (Richard Hughes) - - Automatically download metadata using fwupdmgr if required (Richard Hughes) - - Do not return NULL as a gboolean (Thomas Hindoe Paaboel Andersen) - - Don't call efibootmgr after fwupdate (Mario Limonciello) - - Fallback to offline install when calling the update argument (Mario Limonciello) - - Fix Intel VBIOS detection on Dell hardware (Richard Hughes) - - Reload appstream data after refreshing (Mario Limonciello) - - Use the new LVFS GPG key (Richard Hughes) - - Fix build: libgusb is required even without colorhug support (Jussi Kukkonen) - -Version 0.1.3 -~~~~~~~~~~~~~ -Released: 2015-05-28 - -New Features: - - Get the firmware version from the device descriptors (Richard Hughes) - - Run the offline actions using systemd when required (Richard Hughes) - - Support OpenHardware devices using the fwupd vendor extensions (Richard Hughes) - -Bugfixes: - - Add an UNKNOWN status so we can return meaningful enum values (Richard Hughes) - - Coldplug the devices before acquiring the well known name (Richard Hughes) - -Version 0.1.2 -~~~~~~~~~~~~~ -Released: 2015-04-22 - - Add some guidelines for vendors to README (Richard Hughes) - - Only allow signed firmware to be upgraded without a password (Richard Hughes) - -Version 0.1.1 -~~~~~~~~~~~~~ -Released: 2015-03-23 - -New Features: - - Add a 'get-updates' command to fwupdmgr (Richard Hughes) - - Add and document the offline-update lifecycle (Richard Hughes) - - Create a libfwupd shared library (Richard Hughes) - -Bugfixes: - - Create runtime directories if they do not exist (Richard Hughes) - - Do not crash when there are no devices to return (Richard Hughes) - -Version 0.1.0 -~~~~~~~~~~~~~ -Released: 2015-03-16 - -Notes: - - fwupd is a simple daemon to allow session software to update firmware. diff --git a/README.md b/README.md index 00c2567f2..5fe4e5744 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ fwupd [![Build Status](https://travis-ci.org/hughsie/fwupd.png?branch=master)](https://travis-ci.org/hughsie/fwupd) [![Coverity Scan Build Status](https://scan.coverity.com/projects/10744/badge.svg)](https://scan.coverity.com/projects/10744) +[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-white.svg)](https://snapcraft.io/fwupd) + This project aims to make updating firmware on Linux automatic, safe and reliable. Additional information is available at the website: https://fwupd.org @@ -73,14 +75,19 @@ To clear the local history of updates: Other frontends ------------------- -Currently [GNOME Software](https://wiki.gnome.org/Apps/Software) is the only graphical -frontend available. When compiled with firmware support, it will check for updates -periodically and automatically download firmware in the background. + 1. [GNOME Software](https://wiki.gnome.org/Apps/Software) is the graphical + frontend available. When compiled with firmware support, it will check for + updates periodically and automatically download firmware in the background. + After the firmware has been downloaded a popup will be displayed in Gnome + Software to perform the update. -After the firmware has been downloaded a popup will be displayed in Gnome Software -to perform the update. +2. [KDE Discover](https://userbase.kde.org/Discover) is the software centre, + generally bundled with KDE Plasma. With the release of + [KDE Plasma 5.14](https://www.kde.org/announcements/plasma-5.14.0.php), + a new fwupd backend has been implemented in KDE Discover for firmware updates. + These firmware updates are shown with other system updates. -On Dell IoT gateways, [Wyse Cloud Client Manager (CCM)](http://www.dell.com/us/business/p/wyse-cloud-client-manager/pd) -has been built with fwupd support. -The remote administration interface can be used to download and deploy -firmware updates. +3. [Wyse Cloud Client Manager (CCM)](http://www.dell.com/us/business/p/wyse-cloud-client-manager/pd) + A software suite available on Dell IoT gateways with built-in fwupd support. + The remote administration interface can be used to download and deploy firmware + updates. diff --git a/RELEASE b/RELEASE index 7031df496..302564983 100644 --- a/RELEASE +++ b/RELEASE @@ -1,20 +1,23 @@ fwupd Release Notes -1. Write NEWS entries for fwupd in the same format as usual. +Write release entries: -git shortlog 1.1.3.. | grep -i -v trivial | grep -v Merge > NEWS.new +git log --format="%s" --cherry-pick --right-only 1.2.3... | grep -i -v trivial | grep -v Merge | sort | uniq +Add any user visible changes into ../data/org.freedesktop.fwupd.metainfo.xml +appstream-util appdata-to-news ../data/org.freedesktop.fwupd.metainfo.xml > NEWS -Version 1.1.4 -~~~~~~~~~~~~~ -Released: 2018-xx-xx +Update translations: -New Features: -Bugfixes: +ninja-build fwupd-pot +tx push --source +tx pull --all --force --minimum-perc=5 +../contrib/fix_translations.py ../po +git add ../po/*.po 2. Commit changes to git: -# MAKE SURE THESE ARE CORRECT -export release_ver="1.1.4" +# MAKE SURE THIS IS CORRECT +export release_ver="1.2.4" git commit -a -m "Release fwupd ${release_ver}" git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" diff --git a/contrib/PKGBUILD b/contrib/PKGBUILD index 3e0d6f032..013337491 100644 --- a/contrib/PKGBUILD +++ b/contrib/PKGBUILD @@ -8,7 +8,8 @@ pkgdesc='A simple daemon to allow session software to update firmware' arch=('i686' 'x86_64') url='https://github.com/hughsie/fwupd' license=('GPL2') -depends=('appstream-glib' 'libgusb') +depends=('libgusb') +optdepends=('tpm2-abrmd' 'tpm2-tools') makedepends=('meson' 'valgrind' 'gobject-introspection' 'gtk-doc' 'python-pillow' 'git' 'python-cairo' 'noto-fonts' 'noto-fonts-cjk' 'python-gobject' 'vala' 'libsoup' 'polkit' 'gcab') diff --git a/contrib/ci/Dockerfile-centos.in b/contrib/ci/Dockerfile-centos.in index 76fabcc2c..cd79a76f9 100644 --- a/contrib/ci/Dockerfile-centos.in +++ b/contrib/ci/Dockerfile-centos.in @@ -7,7 +7,6 @@ RUN echo fubar > /etc/machine-id RUN yum install epel-release -y RUN echo fubar > /etc/machine-id %%%INSTALL_DEPENDENCIES_COMMAND%%% -RUN yum install -y https://kojipkgs.fedoraproject.org//packages/libstemmer/0/10.585svn.fc29/x86_64/libstemmer-0-10.585svn.fc29.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/libappstream-glib/0.7.7/3.fc29/x86_64/libappstream-glib-0.7.7-3.fc29.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/libappstream-glib/0.7.7/3.fc29/x86_64/libappstream-glib-devel-0.7.7-3.fc29.x86_64.rpm RUN pip3 install pillow pygobject RUN wget https://copr.fedorainfracloud.org/coprs/jsynacek/systemd-backports-for-centos-7/repo/epel-7/jsynacek-systemd-backports-for-centos-7-epel-7.repo -O /etc/yum.repos.d/jsynacek-systemd-centos-7.repo RUN yum --enablerepo=epel-testing -y update diff --git a/contrib/ci/Dockerfile-fedora.in b/contrib/ci/Dockerfile-fedora.in index ec3d87b0d..6f6994316 100644 --- a/contrib/ci/Dockerfile-fedora.in +++ b/contrib/ci/Dockerfile-fedora.in @@ -1,10 +1,10 @@ -FROM fedora:28 +FROM fedora:29 %%%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 --enablerepo=updates-testing -y update +RUN dnf -y update RUN echo fubar > /etc/machine-id %%%INSTALL_DEPENDENCIES_COMMAND%%% RUN mkdir /build diff --git a/contrib/ci/Dockerfile-flatpak.in b/contrib/ci/Dockerfile-flatpak.in index 09b9ef951..6c4969408 100644 --- a/contrib/ci/Dockerfile-flatpak.in +++ b/contrib/ci/Dockerfile-flatpak.in @@ -1,11 +1,12 @@ -FROM fedora:28 +FROM fedora:29 %%%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 %%%INSTALL_DEPENDENCIES_COMMAND%%% +RUN dnf --enablerepo=updates-testing -y update RUN mkdir /build WORKDIR /build COPY . . -CMD ["./contrib/ci/flatpak.sh"] +CMD ["./contrib/ci/flatpak.py"] diff --git a/contrib/ci/debian_s390x.sh b/contrib/ci/debian_s390x.sh index c65f877ae..25b31ba7a 100755 --- a/contrib/ci/debian_s390x.sh +++ b/contrib/ci/debian_s390x.sh @@ -16,6 +16,8 @@ meson .. \ -Dplugin_redfish=false \ -Dintrospection=false \ -Dgtkdoc=false \ + -Dlibxmlb:introspection=false \ + -Dlibxmlb:gtkdoc=false \ -Dman=false ninja -v ninja test -v diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml index a2dbbec82..e96cacdb7 100644 --- a/contrib/ci/dependencies.xml +++ b/contrib/ci/dependencies.xml @@ -366,15 +366,6 @@ gnu-efi - - - - - - - - - @@ -551,30 +542,34 @@ - + - appstream-glib + libxmlb + - libappstream-glib-devel + libxmlb-devel + @@ -1052,6 +1047,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -1093,10 +1109,20 @@ - + libuuid-devel + + + + uuid-dev:s390x + + + + + + diff --git a/contrib/ci/fedora.sh b/contrib/ci/fedora.sh index f3ab49df7..cb3d0a430 100755 --- a/contrib/ci/fedora.sh +++ b/contrib/ci/fedora.sh @@ -49,12 +49,22 @@ dnf install -y $HOME/rpmbuild/RPMS/*/*.rpm cp $HOME/rpmbuild/RPMS/*/*.rpm dist -# run the installed tests if [ "$CI" = "true" ]; then sed "s,^BlacklistPlugins=test,BlacklistPlugins=," -i /etc/fwupd/daemon.conf + + # 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 + + # run the installed tests whilst the daemon debugging + /usr/libexec/fwupd/fwupd --verbose & + sleep 10 gnome-desktop-testing-runner fwupd fi diff --git a/contrib/ci/flatpak.py b/contrib/ci/flatpak.py new file mode 100755 index 000000000..91aa2bfa1 --- /dev/null +++ b/contrib/ci/flatpak.py @@ -0,0 +1,81 @@ +#!/usr/bin/python3 +import subprocess +import os +import json +import shutil + +def prepare (target): + #clone the flatpak json + cmd = ['git', 'submodule', 'update', '--remote', 'contrib/flatpak'] + subprocess.run (cmd, check=True) + + #clone the submodules for that + cmd = ['git', 'submodule', 'update', '--init', '--remote', 'shared-modules/'] + subprocess.run (cmd, cwd='contrib/flatpak', check=True) + + #parse json + if os.path.isdir ('build'): + shutil.rmtree ('build') + data = {} + with open ('contrib/flatpak/org.freedesktop.fwupd.json', 'r') as rfd: + data = json.load (rfd) + platform = 'runtime/%s/x86_64/%s' % (data['runtime'], data['runtime-version']) + sdk = 'runtime/%s/x86_64/%s' % (data['sdk'], data['runtime-version']) + num_modules = len (data['modules']) + + #update to build from master + data["branch"] = "master" + for index in range(0, num_modules): + module = data['modules'][index] + if type (module) != dict or not 'name' in module: + continue + name = module['name'] + if not 'fwupd' in name: + continue + data['modules'][index]['sources'][0].pop ('url') + data['modules'][index]['sources'][0].pop ('sha256') + data['modules'][index]['sources'][0]['type'] = 'dir' + data['modules'][index]['sources'][0]['skip'] = [".git"] + data['modules'][index]['sources'][0]['path'] = ".." + + #write json + os.mkdir('build') + with open (target, 'w') as wfd: + json.dump(data, wfd, indent=4) + os.symlink ('../contrib/flatpak/shared-modules','build/shared-modules') + + # install the runtimes (parsed from json!) + repo = 'flathub' + repo_url = 'https://dl.flathub.org/repo/flathub.flatpakrepo' + print ("Installing dependencies") + cmd = ['flatpak', 'remote-add', '--if-not-exists', repo, repo_url] + subprocess.run (cmd, check=True) + cmd = ['flatpak', 'install', '--assumeyes', repo, sdk] + subprocess.run (cmd, check=True) + cmd = ['flatpak', 'install', '--assumeyes', repo, platform] + subprocess.run (cmd, check=True) + + +def build (target): + cmd = ['flatpak-builder', '--repo=repo', '--force-clean', '--disable-rofiles-fuse', 'build-dir', target] + subprocess.run (cmd, check=True) + cmd = ['flatpak', 'build-bundle', 'repo', 'fwupd.flatpak', 'org.freedesktop.fwupd'] + subprocess.run (cmd, check=True) + +if __name__ == '__main__': + t = os.path.join ('build', 'org.freedesktop.fwupd.json') + prepare (t) + build (t) + +# to run from the builddir: +# sudo flatpak-builder --run build-dir org.freedesktop.fwupd.json /app/libexec/fwupd/fwupdtool get-devices + +# install the single file bundle +# flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo +# flatpak install fwupd.flatpak + +# to run a shell in the same environment that flatpak sees: +# flatpak run --command=sh --devel org.freedesktop.fwupd + +# to run fwupdtool as root: +# sudo flatpak run org.freedesktop.fwupd --verbose get-devices diff --git a/contrib/ci/flatpak.sh b/contrib/ci/flatpak.sh deleted file mode 100755 index 425299e13..000000000 --- a/contrib/ci/flatpak.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -set -e -set -x - -# install the runtimes -flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo -flatpak install --assumeyes flathub runtime/org.gnome.Sdk/x86_64/3.28 -flatpak install --assumeyes flathub runtime/org.gnome.Platform/x86_64/3.28 - -# build the repo -flatpak-builder --repo=repo --force-clean --disable-rofiles-fuse build-dir contrib/org.freedesktop.fwupd.json - -# show the files that were included -tree build-dir - -# build a single file bundle -flatpak build-bundle repo fwupd.flatpak org.freedesktop.fwupd - -# make available as a deliverable -cp fwupd.flatpak dist - -# to run from the builddir: -# sudo flatpak-builder --run build-dir org.freedesktop.fwupd.json /app/libexec/fwupd/fwupdtool get-devices - -# install the single file bundle -# flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo -# flatpak install fwupd.flatpak - -# to run a shell in the same environment that flatpak sees: -# flatpak run --command=sh --devel org.freedesktop.fwupd - -# to run fwupdtool as root: -# sudo flatpak run org.freedesktop.fwupd --verbose get-devices diff --git a/contrib/firmware-packager/README.md b/contrib/firmware-packager/README.md index db066d79d..894a94bff 100644 --- a/contrib/firmware-packager/README.md +++ b/contrib/firmware-packager/README.md @@ -26,21 +26,19 @@ bin file inside the archive, I would pass `--exe dell-thunderbolt-firmware.exe - ## Documentation -`--firmware-id` ID for the firmware package, can be a customized [fwupd.org](http://fwupd.org/vendors.html) recommends using "a reverse-DNS prefix similar to java" and to "always use a .firmware suffix" (e.g. net.queuecumber.DellTBT.firmware) **REQUIRED** - `--firmware-name` Short name of the firmware package can be customized (e.g. DellTBT) **REQUIRED** `--firmware-summary` One line description of the firmware package (e.g. Dell thunderbolt firmware) `--firmware-description` Longer description of the firmware package. Theoretically this can include HTML but I haven't tried it -`--device-unique-id` Unique ID of the device this firmware will run on, this *must* match the output from `fwupdmgr get-devices` (e.g. 72533768-6a6c-5c06-994a-367374336810) **REQUIRED** +`--device-guid` GUID ID of the device this firmware will run on, this *must* match the output from `fwupdmgr get-devices` (e.g. 72533768-6a6c-5c06-994a-367374336810) **REQUIRED** `--firmware-homepage` Website for the firmware provider (e.g. http://www.dell.com) `-contact-info` Email address of the firmware developer (e.g. someone@something.net) -`--developer-name` Name of the firmware developer (e.g. John Smith) +`--developer-name` Name of the firmware developer (e.g. Dell) **REQUIRED** `--release-version` Version number of the firmware package (e.g. 4.21.01.002) **REQUIRED** `--release-description` Description of the firmware release, again this can theoretically include HTML but I didnt try it. diff --git a/contrib/firmware-packager/firmware-packager b/contrib/firmware-packager/firmware-packager index 531d4f958..e6065d766 100755 --- a/contrib/firmware-packager/firmware-packager +++ b/contrib/firmware-packager/firmware-packager @@ -24,14 +24,14 @@ def cd(path): firmware_metainfo_template = """ - {firmware_id} + org.{developer_name}.guid{firmware_id} {firmware_name} {firmware_summary} {firmware_description} - {device_unique_id} + {device_guid} {firmware_homepage} CC0-1.0 @@ -50,7 +50,9 @@ firmware_metainfo_template = """ def make_firmware_metainfo(firmware_info, dst): - firmware_metainfo = firmware_metainfo_template.format(**vars(firmware_info), timestamp=time.time()) + 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()) with open(os.path.join(dst, 'firmware.metainfo.xml'), 'w') as f: f.write(firmware_metainfo) @@ -68,7 +70,15 @@ def get_firmware_bin(root, bin_path, dst): def create_firmware_cab(exe, folder): with cd(folder): - command = ['gcab', '--create', 'firmware.cab', 'firmware.bin', 'firmware.metainfo.xml'] + if os.name is "nt": + directive = os.path.join (folder, "directive") + with open (directive, 'w') as wfd: + wfd.write('"firmware.cab"\r\n') + wfd.write('"firmware.bin"\r\n') + wfd.write('"firmware.metainfo.xml"\r\n') + command = ['makecab.exe', '/f', directive] + else: + command = ['gcab', '--create', 'firmware.cab', 'firmware.bin', 'firmware.metainfo.xml'] subprocess.check_call(command) @@ -93,18 +103,17 @@ def main(args): shutil.copy(os.path.join(dir, 'firmware.cab'), args.out) parser = argparse.ArgumentParser(description='Create fwupd packaged from windows executables') -parser.add_argument('--firmware-id', help='ID for the firmware package, can be a customized (e.g. net.queuecumber.DellTBT.firmware)', required=True) parser.add_argument('--firmware-name', help='Name of the firmware package can be customized (e.g. DellTBT)', required=True) parser.add_argument('--firmware-summary', help='One line description of the firmware package') parser.add_argument('--firmware-description', help='Longer description of the firmware package') -parser.add_argument('--device-unique-id', help='Unique ID of the device this firmware will run on, this *must* match the output from `fwupdmgr get-devices`', required=True) +parser.add_argument('--device-guid', help='GUID of the device this firmware will run on, this *must* match the output of one of the GUIDs in `fwupdmgr get-devices`', required=True) 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') +parser.add_argument('--developer-name', help='Name of the firmware developer', required=True) parser.add_argument('--release-version', help='Version number of the firmware package', required=True) parser.add_argument('--release-description', help='Description of the firmware release') -parser.add_argument('--exe', help='Executable file to extract firmware from') -parser.add_argument('--bin', help='Path to the .bin file inside the executable to use as the firmware image', required=True) +parser.add_argument('--exe', help='(optional) Executable file to extract firmware from') +parser.add_argument('--bin', help='Path to the .bin file (Relative if inside the executable; Absolute if outside) to use as the firmware image', required=True) parser.add_argument('--out', help='Output cab file path', required=True) args = parser.parse_args() diff --git a/contrib/fix_translations.py b/contrib/fix_translations.py new file mode 100755 index 000000000..8014fb11d --- /dev/null +++ b/contrib/fix_translations.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1+ + +import sys +import os +import subprocess + +def _do_msgattrib(fn): + argv = ['msgattrib', + '--no-location', + '--translated', + '--no-wrap', + '--sort-output', + fn, + '--output-file=' + fn] + ret = subprocess.run(argv) + if ret.returncode != 0: + return + +def _do_nukeheader(fn): + clean_lines = [] + with open(fn) as f: + lines = f.readlines() + for line in lines: + if line.startswith('"POT-Creation-Date:'): + continue + if line.startswith('"PO-Revision-Date:'): + continue + if line.startswith('"Last-Translator:'): + continue + clean_lines.append(line) + with open(fn, 'w') as f: + f.writelines(clean_lines) + +def _process_file(fn): + _do_msgattrib(fn) + _do_nukeheader(fn) + +if __name__ == '__main__': + if len(sys.argv) == 1: + print('path required') + sys.exit(1) + try: + dirname = sys.argv[1] + for fn in os.listdir(dirname): + if fn.endswith('.po'): + _process_file(os.path.join(dirname, fn)) + except NotADirectoryError as _: + print('path required') + sys.exit(2) diff --git a/contrib/flatpak/pip-Makefile b/contrib/flatpak/pip-Makefile deleted file mode 100644 index 13387d906..000000000 --- a/contrib/flatpak/pip-Makefile +++ /dev/null @@ -1,5 +0,0 @@ -all: - python3 setup.py build - -install: - python3 setup.py install --prefix=/app ${ARGS} diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 92571221c..e17976c4e 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -1,5 +1,5 @@ %global glib2_version 2.45.8 -%global libappstream_version 0.7.4 +%global libxmlb_version 0.1.3 %global libgusb_version 0.2.11 %global libsoup_version 2.51.92 %global systemd_version 231 @@ -36,7 +36,7 @@ Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}. BuildRequires: gettext BuildRequires: glib2-devel >= %{glib2_version} -BuildRequires: libappstream-glib-devel >= %{libappstream_version} +BuildRequires: libxmlb-devel >= %{libxmlb_version} BuildRequires: libgcab1-devel BuildRequires: libgudev1-devel BuildRequires: libgusb-devel >= %{libgusb_version} @@ -89,12 +89,14 @@ Requires(preun): systemd Requires(postun): systemd Requires: glib2%{?_isa} >= %{glib2_version} -Requires: libappstream-glib%{?_isa} >= %{libappstream_version} +Requires: libxmlb%{?_isa} >= %{libxmlb_version} Requires: libgusb%{?_isa} >= %{libgusb_version} Requires: libsoup%{?_isa} >= %{libsoup_version} Requires: bubblewrap +Requires: shared-mime-info Recommends: python3 +Recommends: tpm2-tools tpm2-abrmd Obsoletes: fwupd-sign < 0.1.6 Obsoletes: libebitdo < 0.7.5-3 @@ -201,7 +203,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %systemd_postun_with_restart pesign.service %files -f %{name}.lang -%doc README.md AUTHORS NEWS +%doc README.md AUTHORS %license COPYING %config(noreplace)%{_sysconfdir}/fwupd/daemon.conf %if 0%{?have_uefi} @@ -226,6 +228,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %config(noreplace)%{_sysconfdir}/fwupd/remotes.d/lvfs.conf %config(noreplace)%{_sysconfdir}/fwupd/remotes.d/lvfs-testing.conf %config(noreplace)%{_sysconfdir}/fwupd/remotes.d/vendor.conf +%config(noreplace)%{_sysconfdir}/fwupd/remotes.d/vendor-directory.conf %config(noreplace)%{_sysconfdir}/pki/fwupd %{_sysconfdir}/pki/fwupd-metadata %{_sysconfdir}/dbus-1/system.d/org.freedesktop.fwupd.conf @@ -255,6 +258,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %dir %{_libdir}/fwupd-plugins-3 %{_libdir}/fwupd-plugins-3/libfu_plugin_altos.so %{_libdir}/fwupd-plugins-3/libfu_plugin_amt.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_ata.so %{_libdir}/fwupd-plugins-3/libfu_plugin_colorhug.so %{_libdir}/fwupd-plugins-3/libfu_plugin_csr.so %if 0%{?have_dell} @@ -264,6 +268,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %{_libdir}/fwupd-plugins-3/libfu_plugin_dell_dock.so %{_libdir}/fwupd-plugins-3/libfu_plugin_dfu.so %{_libdir}/fwupd-plugins-3/libfu_plugin_ebitdo.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_fastboot.so %{_libdir}/fwupd-plugins-3/libfu_plugin_flashrom.so %{_libdir}/fwupd-plugins-3/libfu_plugin_nitrokey.so %if 0%{?have_uefi} @@ -290,7 +295,8 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %endif %{_libdir}/fwupd-plugins-3/libfu_plugin_unifying.so %{_libdir}/fwupd-plugins-3/libfu_plugin_upower.so -%{_libdir}/fwupd-plugins-3/libfu_plugin_wacomhid.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_wacom_raw.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_wacom_usb.so %ghost %{_localstatedir}/lib/fwupd/gnupg %if 0%{?have_uefi} %{_datadir}/locale/*/LC_IMAGES/fwupd* @@ -306,12 +312,13 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %files tests %dir %{_datadir}/installed-tests/fwupd -%{_datadir}/installed-tests/fwupd/firmware-example.xml.gz -%{_datadir}/installed-tests/fwupd/firmware-example.xml.gz.asc +%{_datadir}/installed-tests/fwupd/fwupd-tests.xml %{_datadir}/installed-tests/fwupd/*.test %{_datadir}/installed-tests/fwupd/*.cab %{_datadir}/installed-tests/fwupd/*.sh %{_datadir}/installed-tests/fwupd/*.py* +%dir %{_sysconfdir}/fwupd/remotes.d +%config(noreplace)%{_sysconfdir}/fwupd/remotes.d/fwupd-tests.conf %changelog * #LONGDATE# Richard Hughes #VERSION#-0.#BUILD##ALPHATAG# diff --git a/contrib/org.freedesktop.fwupd.json b/contrib/org.freedesktop.fwupd.json deleted file mode 100644 index 8f63e2af7..000000000 --- a/contrib/org.freedesktop.fwupd.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "app-id": "org.freedesktop.fwupd", - "runtime": "org.gnome.Platform", - "runtime-version": "3.28", - "branch": "master", - "sdk": "org.gnome.Sdk", - "command": "/app/libexec/fwupd/fwupdtool", - "finish-args": [ - "--device=all", - "--filesystem=/boot", - "--filesystem=/sys", - "--filesystem=xdg-download", - "--share=network", - "--system-talk-name=org.freedesktop.fwupd", - "--system-talk-name=org.freedesktop.UPower" - ], - "build-options": { - "cflags": "-O2 -g", - "cxxflags": "-O2 -g" - }, - "cleanup": [ - "*.a", - "*.la", - "/include", - "/lib/girepository-1.0", - "/lib/pkgconfig", - "/share/bash-completion", - "/share/dbus-1/system-services", - "/share/gir-1.0", - "/share/gtk-doc", - "/share/info", - "/share/man", - "/share/pkgconfig" - ], - "modules": [ - { - /* not using shared-modules as we need gudev */ - "name": "udev", - "rm-configure": true, - "config-opts": [ - "--disable-hwdb", - "--disable-logging", - "--disable-introspection", - "--disable-keymap", - "--disable-mtd_probe" - ], - "cleanup": [ - "/etc/udev", - "/libexec/*", - "/share/gtk-doc/html/libudev/", - "/sbin/udevadm" - ], - "sources": [ - { - "type": "archive", - "url": "http://kernel.org/pub/linux/utils/kernel/hotplug/udev-175.tar.bz2", - "sha256": "4c7937fe5a1521316ea571188745b9a00a9fdf314228cffc53a7ba9e5968b7ab" - }, - { - "type": "script", - "dest-filename": "autogen.sh", - "commands": [ - "autoreconf -vfi" - ] - } - ] - }, - { - "name": "libusb", - "config-opts": [ - "--disable-static" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2", - "sha256": "75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157" - } - ] - }, - { - "name": "gusb", - "buildsystem": "meson", - "config-opts": [ - "-Ddocs=false", - "-Dvapi=false", - "-Dtests=false" - ], - "cleanup": [ - "/bin/gusbcmd" - ], - "sources": [ - { - "type": "archive", - "url": "https://people.freedesktop.org/~hughsient/releases/libgusb-0.3.0.tar.xz", - "sha256": "d8e7950f99b6ae4c3e9b8c65f3692b9635289e6cff8de40c4af41b2e9b348edc" - } - ] - }, - { - "name": "efivar", - "buildsystem": "simple", - "build-commands": [ - "make prefix=/app libdir=/app/lib", - "make install prefix=/app libdir=/app/lib" - ], - "cleanup": [ - "/bin/efivar" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/rhboot/efivar/releases/download/35/efivar-35.tar.bz2", - "sha256": "1e033dc5d099a44fd473b0887dbcc4b105613efab0fb3c5df9f111ea5d147394" - } - ] - }, - { - "name": "libsmbios_c", - "only-arches": [ - "x86_64" - ], - "config-opts": [ - "--disable-doxygen", - "--disable-graphviz", - "--disable-python" - ], - "cleanup": [ - "/sbin/smbios*", - "/share/locale/*/LC_MESSAGES/libsmbios.mo" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/dell/libsmbios/archive/v2.4.2.tar.gz", - "sha256": "ebfe18415e24bbec06d0a9ea1066c8dcd82982555373712713d7e194138650de" - } - ] - }, - { - "name": "gnu-efi", - "only-arches": [ - "aarch64", - "x86_64" - ], - "buildsystem": "simple", - "build-commands": [ - "make", - "make PREFIX=/app install" - ], - "no-autogen": true, - "cleanup": [ - "/bin/efivar" - ], - "sources": [ - { - "type": "archive", - "url": "http://superb-dca2.dl.sourceforge.net/project/gnu-efi/gnu-efi-3.0.5.tar.bz2", - "sha256": "bd8fcd5914f18fc0e4ba948ab03b00013e528504f529c60739b748f6ef130b22" - } - ] - }, - { - "name": "python3-olefile", - "only-arches": [ - "aarch64", - "x86_64" - ], - "buildsystem": "simple", - "build-commands": [ - "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} olefile" - ], - "sources": [ - { - "type": "file", - "url": "https://pypi.python.org/packages/35/17/c15d41d5a8f8b98cc3df25eb00c5cee76193114c78e5674df6ef4ac92647/olefile-0.44.zip", - "sha256": "61f2ca0cd0aa77279eb943c07f607438edf374096b66332fae1ee64a6f0f73ad" - } - ] - }, - { - "name": "python3-pillow", - "only-arches": [ - "aarch64", - "x86_64" - ], - "buildsystem": "simple", - "build-commands": [ - "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} Pillow" - ], - "cleanup": [ - "/bin/*.py" - ], - "sources": [ - { - "type": "file", - "url": "https://pypi.python.org/packages/93/73/66854f63b1941aad9af18a1de59f9cf95ad1a87c801540222e332f6688d7/Pillow-4.1.1.tar.gz", - "sha256": "00b6a5f28d00f720235a937ebc2f50f4292a5c7e2d6ab9a8b26153b625c4f431" - } - ] - }, - { - "name": "fwupd", - "buildsystem": "meson", - "config-opts": [ - "-Dconsolekit=false", - "-Ddaemon=false", - "-Dgpg=false", - "-Dgtkdoc=false", - "-Dintrospection=false", - "-Dman=false", - "-Dpkcs7=false", - "-Dsystemd=false", - "-Dtests=false", - "-Defi-includedir=/app/include/efi", - "-Defi-ldsdir=/app/lib", - "-Defi-libdir=/app/lib", - "--sysconfdir=/app/etc", - "--localstatedir=/var/data" - ], - "build-options" : { - "arch": { - "i386": { - "config-opts": [ - "-Dplugin_dell=false", - "-Dplugin_uefi=false" - ] - }, - "arm": { - "config-opts": [ - "-Dplugin_dell=false", - "-Dplugin_uefi=false" - ] - }, - "aarch64": { - "config-opts": [ - "-Dplugin_dell=false" - ] - } - } - }, - "cleanup": [ - "/etc/dbus-1/system.d/org.freedesktop.fwupd.conf", - "/share/fwupd/remotes.d/vendor" - ], - "sources": [ - { - "type": "dir", - "skip": [".git"], - "path": ".." - } - ] - } - ] -} diff --git a/contrib/simple_client.py b/contrib/simple_client.py new file mode 100755 index 000000000..0865a2e61 --- /dev/null +++ b/contrib/simple_client.py @@ -0,0 +1,126 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1+ +"""A simple fwupd frontend""" +import sys +import os +import gi +from gi.repository import GLib +gi.require_version('Fwupd', '2.0') +from gi.repository import Fwupd #pylint: disable=wrong-import-position + +class Progress(): + """Class to track the signal changes of progress events""" + def __init__(self): + self.device = None + self.status = None + self.percent = 0 + self.erase = 0 + + def device_changed(self, new_device): + """Indicate new device string to track""" + if self.device != new_device: + self.device = new_device + print("\nUpdating %s" % self.device) + + def status_changed(self, percent, status): + """Indicate new status string or % complete to track""" + if self.status != status or self.percent != percent: + for i in range(0, self.erase): + sys.stdout.write("\b \b") + self.status = status + self.percent = percent + status_str = "[" + for i in range(0, 50): + if i < percent/2: + status_str += '*' + else: + status_str += ' ' + status_str += "] %d%% %s" %(percent, status) + status_str.erase = len(status_str) + sys.stdout.write(status_str) + sys.stdout.flush() + if 'idle' in status: + sys.stdout.write("\n") + +def parse_args(): + """Parse arguments for this client""" + import argparse + parser = argparse.ArgumentParser(description="Interact with fwupd daemon") + parser.add_argument("--allow-older", action="store_true", + help="Install older payloads(default False)") + parser.add_argument("--allow-reinstall", action="store_true", + help="Reinstall payloads(default False)") + parser.add_argument("command", choices=["get-devices", + "get-details", + "install"], help="What to do") + parser.add_argument('cab', nargs='?', help='CAB file') + parser.add_argument('deviceid', nargs='?', + help='DeviceID to operate on(optional)') + args = parser.parse_args() + return args + +def get_devices(client): + """Use fwupd client to fetch devices""" + devices = client.get_devices() + for item in devices: + print(item.to_string()) + +def get_details(client, cab): + """Use fwupd client to fetch details for a CAB file""" + devices = client.get_details(cab, None) + for device in devices: + print(device.to_string()) + +def status_changed(client, spec, progress): #pylint: disable=unused-argument + """Signal emitted by fwupd daemon indicating status changed""" + progress.status_changed(client.get_percentage(), + Fwupd.status_to_string(client.get_status())) + +def device_changed(client, device, progress): #pylint: disable=unused-argument + """Signal emitted by fwupd daemon indicating active device changed""" + progress.device_changed(device.get_name()) + +def install(client, cab, target, older, reinstall): + """Use fwupd client to install CAB file to applicable devices""" + # FWUPD_DEVICE_ID_ANY + if not target: + target = '*' + flags = Fwupd.InstallFlags.NONE + if older: + flags |= Fwupd.InstallFlags.ALLOW_OLDER + if reinstall: + flags |= Fwupd.InstallFlags.ALLOW_REINSTALL + progress = Progress() + parent = super(client.__class__, client) + parent.connect('device-changed', device_changed, progress) + parent.connect('notify::percentage', status_changed, progress) + parent.connect('notify::status', status_changed, progress) + try: + client.install(target, cab, flags, None) + except GLib.Error as glib_err: #pylint: disable=catching-non-exception + progress.status_changed(0, 'idle') + print("%s" % glib_err) + sys.exit(1) + +def check_cab(cab): + """Check that CAB file exists""" + if not cab: + print("Need to specify payload") + sys.exit(1) + if not os.path.isfile(cab): + print("%s doesn't exist or isn't a file" % cab) + sys.exit(1) + +if __name__ == '__main__': + ARGS = parse_args() + CLIENT = Fwupd.Client() + CLIENT.connect() + + if ARGS.command == "get-devices": + get_devices(CLIENT) + elif ARGS.command == "get-details": + check_cab(ARGS.cab) + get_details(CLIENT, ARGS.cab) + elif ARGS.command == "install": + check_cab(ARGS.cab) + install(CLIENT, ARGS.cab, ARGS.deviceid, ARGS.allow_older, ARGS.allow_reinstall) diff --git a/contrib/snap/snapcraft-master.yaml b/contrib/snap/snapcraft-master.yaml index 406cd1c86..8149df842 100644 --- a/contrib/snap/snapcraft-master.yaml +++ b/contrib/snap/snapcraft-master.yaml @@ -71,7 +71,7 @@ parts: meson: plugin: python source: https://github.com/mesonbuild/meson.git - source-tag: 0.46.1 + source-tag: 0.47.2 build-packages: - ninja-build prime: @@ -80,54 +80,6 @@ parts: - -lib - -share - -usr - appstream-glib-dev: - plugin: meson - meson-parameters: [--prefix=/usr, -Dgtk-doc=false, -Dintrospection=false, -Dman=false, -Drpm=false] - source: https://github.com/hughsie/appstream-glib - source-type: git - build-packages: - - python3-pip - - gperf - - intltool - - libarchive-dev - - libgcab-dev - - libgdk-pixbuf2.0-dev - - libgirepository1.0-dev - - libglib2.0-dev - - libgtk-3-dev - - libjson-glib-dev - - libsoup2.4-dev - - libsqlite3-dev - - libyaml-dev - - libstemmer-dev - - uuid-dev - stage-packages: - - libarchive13 - - libgcab-1.0-0 - - libsoup2.4-1 - - libstemmer0d - - libgdk-pixbuf2.0-0 - prime: - - -usr/bin - - -usr/include - - -usr/share/doc - - -usr/lib/*/asb-plugins-5 - - -usr/share/bash-completion - - -usr/share/aclocal - - -usr/lib/*/pkgconfig - - -usr/share/installed-tests - - -usr/lib/systemd - - -usr/lib/glib-networking - - -usr/lib/dconf - - -usr/share/X11 - - -usr/share/GConf - - -usr/share/dbus-1 - - -usr/share/glib-2.0/schemas - - -usr/share/lintian - - -usr/share/man - - -usr/lib/*/gdk-pixbuf-2.0 - - -usr/share/gettext - after: [meson] gudev: plugin: autotools source: https://gitlab.gnome.org/GNOME/libgudev.git @@ -238,6 +190,8 @@ parts: -Dintrospection=false, -Dman=false, -Dudevdir=$SNAPCRAFT_STAGE/lib/udev, + -Dlibxmlb:gtkdoc=false, + -Dlibxmlb:introspection=false, -Dpkcs7=false] source: . source-type: git @@ -310,7 +264,7 @@ parts: - -usr/share/gir-1.0 - -usr/share/upstart - -usr/lib/*/pkgconfig - after: [appstream-glib-dev, gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext] + after: [gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext] fix-bash-completion: plugin: make source: contrib/snap/fix-bash-completion diff --git a/contrib/snap/snapcraft-stable.yaml b/contrib/snap/snapcraft-stable.yaml deleted file mode 100644 index 4537a1167..000000000 --- a/contrib/snap/snapcraft-stable.yaml +++ /dev/null @@ -1,337 +0,0 @@ -name: fwupd -version-script: cat $SNAPCRAFT_STAGE/version -version: 'daily' -summary: A standalone version of fwupd to install newer firmware updates -description: | - This is a tool that can be used to install firmware updates on devices - not yet supported by the version of fwupd distributed with the OS. - -grade: stable -confinement: classic - -architectures: - - amd64 - -apps: - dfu-tool: - command: dfu-tool.wrapper - fwupdtool: - command: fwupdtool.wrapper - completer: - share/bash-completion/completions/fwupdtool - fwupd: - command: fwupd.wrapper - daemon: simple - fwupdmgr: - command: fwupdmgr.wrapper - completer: - share/bash-completion/completions/fwupdmgr - -parts: - libefivar-dev: - plugin: make - make-parameters: - - prefix=/ - - libdir=/lib - source: https://github.com/rhboot/efivar/releases/download/35/efivar-35.tar.bz2 - build-packages: - - libpopt-dev - prime: - - -include - - -bin - - -share/man - - -lib/pkgconfig - #adjust the paths from libefivar - libefivar-fixpkgconfig: - plugin: make - source: contrib/snap/libefivar-fixpkgconfig - make-parameters: - - SNAPCRAFT_STAGE=$SNAPCRAFT_STAGE - after: [libefivar-dev] - libsmbios: - plugin: autotools - source: https://github.com/dell/libsmbios/archive/v2.4.2.tar.gz - build-packages: - - libxml2-dev - - pkg-config - - autoconf - - automake - - libtool - - autopoint - prime: - - -include/ - - -lib/pkgconfig - - -lib/python3.5 - - -sbin/ - - -share/ - - -etc/ - - -lib/*.a - meson: - plugin: python - source: https://github.com/mesonbuild/meson/releases/download/0.46.1/meson-0.46.1.tar.gz - build-packages: - - ninja-build - prime: - - -bin - - -etc - - -lib - - -share - - -usr - appstream-glib-dev: - plugin: meson - meson-parameters: [--prefix=/usr, -Dgtk-doc=false, -Dintrospection=false, -Dman=false, -Drpm=false] - source: https://github.com/hughsie/appstream-glib/archive/appstream_glib_0_7_9.tar.gz - build-packages: - - python3-pip - - gperf - - intltool - - libarchive-dev - - libgcab-dev - - libgdk-pixbuf2.0-dev - - libgirepository1.0-dev - - libglib2.0-dev - - libgtk-3-dev - - libjson-glib-dev - - libsoup2.4-dev - - libsqlite3-dev - - libyaml-dev - - libstemmer-dev - - uuid-dev - stage-packages: - - libarchive13 - - libgcab-1.0-0 - - libsoup2.4-1 - - libstemmer0d - - libgdk-pixbuf2.0-0 - prime: - - -usr/bin - - -usr/include - - -usr/share/doc - - -usr/lib/*/asb-plugins-5 - - -usr/share/bash-completion - - -usr/share/aclocal - - -usr/lib/*/pkgconfig - - -usr/share/installed-tests - - -usr/lib/systemd - - -usr/lib/glib-networking - - -usr/lib/dconf - - -usr/share/X11 - - -usr/share/GConf - - -usr/share/dbus-1 - - -usr/share/glib-2.0/schemas - - -usr/share/lintian - - -usr/share/man - - -usr/lib/*/gdk-pixbuf-2.0 - - -usr/share/gettext - after: [meson] - gudev: - plugin: autotools - source: https://github.com/GNOME/libgudev/archive/232.tar.gz - configflags: - - --disable-umockdev - build-packages: - - libglib2.0-dev - - pkg-config - - libudev-dev - - gtk-doc-tools - - gnome-common - prime: - - -include - - -lib/girepository-1.0 - - -lib/pkgconfig - - -share/ - libusb: - plugin: autotools - source: https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2 - configflags: - - --disable-static - prime: - - -include/ - - -lib/pkgconfig - gusb: - plugin: meson - source: https://github.com/hughsie/libgusb/archive/0.3.0.tar.gz - meson-parameters: [--prefix=/, - -Dtests=false, - -Dvapi=false, - -Ddocs=false] - build-packages: - - libgirepository1.0-dev - prime: - - -bin/ - - -include - - -share - - -lib/*/pkgconfig - - -lib/*/girepository-1.0 - after: [meson, libusb] - gnu-efi: - plugin: make - source: http://superb-dca2.dl.sourceforge.net/project/gnu-efi/gnu-efi-3.0.5.tar.bz2 - make-parameters: - - PREFIX=/usr - make-install-var: INSTALLROOT - prime: - - -usr/include/ - - -usr/lib - #fetch the latest version of the signed bootloader - #this might not match our fwupdx64.efi, but it's better than nothing - fwup-efi-signed: - build-packages: - - python3-apt - plugin: make - source: contrib/snap/fwup-efi-signed - #needed for UEFI plugin to build UX labels - build-introspection: - plugin: nil - stage-packages: - - python3-gi - - python3-gi-cairo - - python3-pil - prime: - - -etc - - -usr - - -lib - - -var - #0.19.8.1 adds support for GETTEXTDATADIRS which is needed by meson's msgfmthelper - gettext: - source: https://ftp.gnu.org/pub/gnu/gettext/gettext-0.19.8.1.tar.xz - plugin: autotools - build-packages: - - bison - - libunistring-dev - - libxml2-dev - configflags: - - --prefix=/usr - - --disable-static - - --disable-curses - - --disable-java - - --enable-relocatable - - --without-emacs - - --without-included-glib - - --without-included-libunistring - - --without-included-libxml - stage-packages: - - libunistring0 - - libxml2 - - libgomp1 - prime: - - -**/*.a - - -**/*.la - - -usr/bin - - -usr/include - - -usr/lib/gettext - - -usr/share - fwupd: - plugin: meson - meson-parameters: [--prefix=/, - -Defi-includedir=$SNAPCRAFT_STAGE/usr/include/efi, - -Defi-ldsdir=$SNAPCRAFT_STAGE/usr/lib, - -Defi-libdir=$SNAPCRAFT_STAGE/usr/lib, - -Dtests=false, - -Ddaemon=true, - -Dgtkdoc=false, - -Dintrospection=false, - -Dman=false, - -Dudevdir=$SNAPCRAFT_STAGE/lib/udev, - -Dpkcs7=false] - source: . - source-type: git - override-build: | - snapcraftctl build - echo $(git describe HEAD --always) > $SNAPCRAFT_STAGE/version - build-packages: - - bash-completion - - gcab - - gnutls-dev - - libarchive-dev - - libcairo-dev - - libelf-dev - - libgcab-dev - - libglib2.0-dev - - libgpgme11-dev - - libjson-glib-dev - - libpango1.0-dev - - libpolkit-gobject-1-dev - - libsoup2.4-dev - - libsqlite3-dev - - locales - - pkg-config - - uuid-dev - stage-packages: - - libgcab-1.0-0 - - libarchive13 - - libassuan0 - - liblcms2-2 - - libelf1 - - libgpgme11 - - libjson-glib-1.0-0 - - libpolkit-gobject-1-0 - - libsoup2.4-1 - - glib-networking - - libglib2.0-bin - prime: - # we explicitly don't want /usr/bin/gpgconf - # this will cause gpgme to error finding it - # but that also avoids trying to use non-existent - # /usr/bin/gpg2 - - -usr/bin - - -usr/sbin - - -usr/share/man - - -usr/share/GConf - - -etc/X11 - - -etc/ldap - - -etc/logcheck - - -usr/lib/dconf - - -usr/lib/gcc - - -usr/lib/glib-networking - - -usr/lib/gnupg2 - - -usr/lib/sasl2 - - -usr/lib/systemd - - -usr/lib/*/audit - - -usr/share/glib-2.0/schemas - - -usr/share/X11 - - -include - - -lib/systemd - - -lib/udev - - -lib/*/pkgconfig - - -usr/share/lintian - - -usr/share/pkgconfig - - -usr/share/installed-tests - - -usr/share/polkit-1 - - -usr/share/vala - - -usr/share/doc - - -usr/share/gnupg2 - - -usr/share/info - - -usr/share/gir-1.0 - - -usr/share/upstart - - -usr/lib/*/pkgconfig - after: [appstream-glib-dev, gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext] - fix-bash-completion: - plugin: make - source: contrib/snap/fix-bash-completion - after: [fwupd] - update-mime: - plugin: make - source: contrib/snap/update-mime - stage-packages: - - shared-mime-info - - gsettings-desktop-schemas - - libxml2 - prime: - - -usr/bin - - -usr/share/doc - - -usr/share/doc-base - - -usr/share/man - - -usr/share/lintian - - -usr/share/pkgconfig - - -usr/share/GConf - after: [fwupd] - fwupd-wrappers: - plugin: dump - source: contrib/snap - stage: - - dfu-tool.wrapper - - fwupd-command - - fwupdtool.wrapper - - fwupd.wrapper - - fwupdmgr.wrapper diff --git a/contrib/standalone-installer/assets/header.py b/contrib/standalone-installer/assets/header.py index 058ee3896..262360b09 100644 --- a/contrib/standalone-installer/assets/header.py +++ b/contrib/standalone-installer/assets/header.py @@ -128,7 +128,7 @@ def install_flatpak (directory, verbose, uninstall): else: output = None #look for dependencies - dep = 'org.gnome.Platform/x86_64/3.28' + dep = 'org.gnome.Platform/x86_64/3.30' repo = 'flathub' repo_url = 'https://flathub.org/repo/flathub.flatpakrepo' cmd = ['flatpak', 'info', dep] diff --git a/contrib/standalone-installer/make.py b/contrib/standalone-installer/make.py index ddd623837..7b76c8f2c 100755 --- a/contrib/standalone-installer/make.py +++ b/contrib/standalone-installer/make.py @@ -85,8 +85,23 @@ def download_cab_file (directory, uri): subprocess.run (cmd, cwd=directory, check=True) def download_flatpak (directory): - cmd = ['wget', 'https://people.freedesktop.org/~hughsient/temp/fwupd.flatpak', '-O', 'fwupd.flatpak'] - if 'DEBUG' in os.environ: + dep = 'org.freedesktop.fwupd' + flatpak_dir = os.path.join(os.getenv('HOME'),'.local', 'share', 'flatpak') + verbose = 'DEBUG' in os.environ + + #check if we have installed locally already or not + if not os.path.exists (os.path.join (flatpak_dir, 'app', dep)): + # install into local user's repo + cmd = ['flatpak', 'install', '--user', + 'https://www.flathub.org/repo/appstream/org.freedesktop.fwupd.flatpakref', '--no-deps', '-y'] + if verbose: + print(cmd) + subprocess.run (cmd, cwd=directory, check=True) + + # generate a bundle + repo = os.path.join(flatpak_dir, 'repo') + cmd = ['flatpak', 'build-bundle', repo, 'fwupd.flatpak', dep, 'stable'] + if verbose: print(cmd) subprocess.run (cmd, cwd=directory, check=True) diff --git a/contrib/vscode/README.md b/contrib/vscode/README.md new file mode 100644 index 000000000..7d9f35f72 --- /dev/null +++ b/contrib/vscode/README.md @@ -0,0 +1,39 @@ +# Using Visual Studio Code to debug + +This directory contains a collection of scripts and assets to make debugging using Visual Studio Code easier. + +## Preparing +First install the following applications locally: +* GDB Server +* GDB +* Visual Studio Code + +In Visual Studio code, visit the extension store and install *C/C++* which is an extension provided by Microsoft. +Configure Visual Studio code to open the folder representing the root of the fwupd checkout. + +## Building +Run `./contrib/debugging/build.sh` to build fwupd with all default options and create helper scripts pre-configured for debugger use. +The application will be placed into `./dist` and helper scripts will be created for `fwupdtool`, `fwupdmgr`, and `fwupd`. + +## Running +To run any of the applications, execute the appropriate helper script in `./dist`. + +## Debugging +To debug any of the applications, launch the helper script with the environment variable `DEBUG` set. +For example to debug `fwupdtool get-devices` the command to launch would be: + +``` +sudo DEBUG=1 ./dist/fwupdtool.sh get-devices +``` + +This will configure `gdbserver` to listen on a local port waiting for a debugger to connect. + +## Using Visual Studio code +During build time a set of launch targets will have been created for use with Visual Studio Code. + +Press the debugging button on the left and 3 targets will be listed at the top. +* gdbserver (fwupdtool) +* gdbserver (fwupd) +* gdbserver (fwupdmgr) + +Select the appropriate target and press the green arrow to connect to `gdbserver` and start debugging. diff --git a/contrib/vscode/build.sh b/contrib/vscode/build.sh new file mode 100755 index 000000000..3420ba56c --- /dev/null +++ b/contrib/vscode/build.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# Copyright (C) 2018 Dell, Inc. + +SOURCE=$(dirname $0) +ROOT=$1 +if [ -z "$ROOT" ]; then + ROOT=`pwd` +fi + +# build in tree +rm -rf build ${ROOT}/dist +meson build --prefix=${ROOT}/dist -Dsystemd=false -Dudevdir=${ROOT}/dist +ninja -C build install + +#create helper scripts +TEMPLATE=${SOURCE}/launcher.sh +sed "s,#ROOT#,${ROOT},; s,#EXECUTABLE#,libexec/fwupd/fwupd," \ + ${TEMPLATE} > ${ROOT}/dist/fwupd.sh +sed "s,#ROOT#,${ROOT},; s,#EXECUTABLE#,libexec/fwupd/fwupdtool," \ + ${TEMPLATE} > ${ROOT}/dist/fwupdtool.sh +sed "s,#ROOT#,${ROOT},; s,#EXECUTABLE#,bin/fwupdmgr," \ + ${TEMPLATE} > ${ROOT}/dist/fwupdmgr.sh +chmod +x ${ROOT}/dist/*.sh + +#create debugging targets +TARGET=${ROOT}/.vscode +mkdir -p ${TARGET} +if [ -f ${TARGET}/launch.json ]; then + echo "${TARGET}/launch.json already exists, not overwriting" +else + cp ${SOURCE}/launch.json ${TARGET} +fi diff --git a/contrib/vscode/launch.json b/contrib/vscode/launch.json new file mode 100644 index 000000000..ad1aaa0b4 --- /dev/null +++ b/contrib/vscode/launch.json @@ -0,0 +1,68 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "gdbserver (fwupdtool)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/dist/libexec/fwupd/fwupdtool", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "miDebuggerServerAddress": "localhost:9091", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "gdbserver (fwupd)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/dist/libexec/fwupd/fwupd", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "miDebuggerServerAddress": "localhost:9091", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "gdbserver (fwupdmgr)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/dist/bin/fwupdmgr", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "miDebuggerServerAddress": "localhost:9091", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + ] +} diff --git a/contrib/vscode/launcher.sh b/contrib/vscode/launcher.sh new file mode 100755 index 000000000..d324080df --- /dev/null +++ b/contrib/vscode/launcher.sh @@ -0,0 +1,8 @@ +#!/bin/sh +export ROOT=#ROOT# +export FWUPD_LOCALSTATEDIR=${ROOT}/dist +export FWUPD_SYSCONFDIR=${ROOT}/dist/etc +if [ -n "${DEBUG}" ]; then + DEBUG="gdbserver localhost:9091" +fi +${DEBUG} ${ROOT}/dist/#EXECUTABLE# "$@" diff --git a/data/bash-completion/fwupdmgr b/data/bash-completion/fwupdmgr index 2e27dadb0..631d09ee5 100644 --- a/data/bash-completion/fwupdmgr +++ b/data/bash-completion/fwupdmgr @@ -1,5 +1,4 @@ _fwupdmgr_cmd_list=( - 'build-firmware' 'clear-history' 'clear-offline' 'clear-results' @@ -17,7 +16,6 @@ _fwupdmgr_cmd_list=( 'install' 'install-prepared' 'modify-remote' - 'monitor' 'refresh' 'report-history' 'unlock' @@ -147,23 +145,6 @@ _fwupdmgr() _show_modifiers fi ;; - build-firmware) - #file in - if [[ "$prev" = "$command" ]]; then - _filedir - #file out - elif [[ "$prev" = "${COMP_WORDS[2]}" ]]; then - _filedir - #script - elif [[ "$prev" = "${COMP_WORDS[3]}" ]]; then - _filedir - #output - elif [[ "$prev" = "${COMP_WORDS[4]}" ]]; then - _filedir - else - _show_modifiers - fi - ;; *) #find first command if [[ ${COMP_CWORD} = 1 ]]; then diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index 6062e2e50..198cd8f97 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -1,11 +1,15 @@ _fwupdtool_cmd_list=( + 'build-firmware' + 'get-updates' 'get-details' 'get-devices' 'get-plugins' 'get-topology' 'hwids' + 'update' 'install' 'install-blob' + 'monitor' 'smbios-dump' 'attach' 'detach' @@ -66,6 +70,23 @@ _fwupdtool() _show_modifiers fi ;; + build-firmware) + #file in + if [[ "$prev" = "$command" ]]; then + _filedir + #file out + elif [[ "$prev" = "${COMP_WORDS[2]}" ]]; then + _filedir + #script + elif [[ "$prev" = "${COMP_WORDS[3]}" ]]; then + _filedir + #output + elif [[ "$prev" = "${COMP_WORDS[4]}" ]]; then + _filedir + else + _show_modifiers + fi + ;; *) #find first command if [[ ${COMP_CWORD} = 1 ]]; then diff --git a/data/daemon.conf b/data/daemon.conf index 51ab19f4c..e5065fe9b 100644 --- a/data/daemon.conf +++ b/data/daemon.conf @@ -10,3 +10,9 @@ BlacklistPlugins=test # Maximum archive size that can be loaded in Mb, with 0 for the default ArchiveSizeMax=0 + +# Idle time in seconds to shut down the daemon -- note some plugins might +# inhibit the auto-shutdown, for instance thunderbolt. +# +# A value of 0 specifies 'never' +IdleTimeout=7200 diff --git a/data/installed-tests/README.md b/data/installed-tests/README.md new file mode 100644 index 000000000..d48d38dd6 --- /dev/null +++ b/data/installed-tests/README.md @@ -0,0 +1,78 @@ +Installed tests +========= + +A test suite that can be used to interact with a fake device is installed when +configured with `-Ddaemon=true` and `-Dtests=true`. + +By default this test suite is disabled. + +Enabling +======= +To enable the test suite: +1. Modify `/etc/fwupd/daemon.conf` to remove the `test` plugin from `BlacklistPlugins` + ``` + # sed "s,^Enabled=false,Enabled=true," -i /etc/fwupd/remotes.d/fwupd-tests.conf + ``` +2. Enable the `fwupd-tests` remote for local CAB files. + ``` + # fwupdmgr enable-remote fwupd-tests + ``` + +Using test suite +===== +When the daemon is started with the test suite enabled a fake webcam device will be created with a pending update. + +``` +Integrated Webcam™ + DeviceId: 08d460be0f1f9f128413f816022a6439e0078018 + Guid: b585990a-003e-5270-89d5-3705a17f9a43 + Summary: A fake webcam + Plugin: test + Flags: updatable|supported|registered + Vendor: ACME Corp. + VendorId: USB:0x046D + Version: 1.2.2 + VersionLowest: 1.2.0 + VersionBootloader: 0.1.2 + Icon: preferences-desktop-keyboard + Created: 2018-11-29 +``` + +## Upgrading +This can be upgraded to a firmware version `1.2.4` by using `fwupdmgr update` or any fwupd frontend. + +``` +$ fwupdmgr get-updates +Integrated Webcam™ has firmware updates: +GUID: b585990a-003e-5270-89d5-3705a17f9a43 +ID: fakedevice.firmware +Update Version: 1.2.4 +Update Name: FakeDevice Firmware +Update Summary: Firmware for the ACME Corp Integrated Webcam +Update Remote ID: fwupd-tests +Update Checksum: SHA1(fc0aabcf98bf3546c91270f2941f0acd0395dd79) +Update Location: ./fakedevice124.cab +Update Description: Fixes another bug with the flux capacitor to prevent time going backwards. + +$ fwupdmgr update +Decompressing… [***************************************] +Authenticating… [***************************************] +Updating Integrated Webcam™… ] +Verifying… [***************************************] Less than one minute remaining… +``` + +## Downgrading +It can also be downgraded to firmware version `1.2.3`. +``` +$ fwupdmgr downgrade +Choose a device: +0. Cancel +1. 08d460be0f1f9f128413f816022a6439e0078018 (Integrated Webcam™) +2. 8a21cacfb0a8d2b30c5ee9290eb71db021619f8b (XPS 13 9370 System Firmware) +3. d10c5f0ed12c6dc773f596b8ac51f8ace4355380 (XPS 13 9370 Thunderbolt Controller) +1 +Decompressing… [***************************************] +Authenticating… [***************************************] +Downgrading Integrated Webcam™… \ ] +Verifying… [***************************************] Less than one minute remaining… +``` diff --git a/data/installed-tests/firmware-example.xml.gz b/data/installed-tests/firmware-example.xml.gz deleted file mode 100644 index f2d02082c..000000000 Binary files a/data/installed-tests/firmware-example.xml.gz and /dev/null differ diff --git a/data/installed-tests/firmware-example.xml.gz.asc b/data/installed-tests/firmware-example.xml.gz.asc deleted file mode 100644 index 158887d1c..000000000 --- a/data/installed-tests/firmware-example.xml.gz.asc +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PGP SIGNATURE----- -Version: GnuPG v2.0.14 (GNU/Linux) - -iQEcBAABAgAGBQJZQqy+AAoJEEim2A5FOLrCjuEH/1wIRvQ9FIUqQ2wV5pQRjF99 -wTd1+VtQCPHkBXvMnrF2cnhNhr13lN8BhuY3kgT9TGQCPNM+8akNvDLKWiR/39rP -z+v6KpgaYA5kghFskvW4t/1lQ+Jj+PKExb1bAusexdVvRD1iEDZ0q8u/DRGwrjYn -GFbHD3K91B4nIzYQVHa8+9gRRH2uKa2U9foH3++e/PAPQCoGHa6gxV3zM05AiEpA -Am5G5u8v5WnL9W9H53unj9M47iAlzdxStzK4poshlJoNITLw9SLl6rmrgMYLHVfD -QyZTM8jSjFc+V1swslLNMVCkCWfx3ClzkEff50HKua/BxMxxJscShX43On59cik= -=XhuO ------END PGP SIGNATURE----- diff --git a/data/installed-tests/fwupd-tests.xml b/data/installed-tests/fwupd-tests.xml new file mode 100644 index 000000000..c24bb3169 --- /dev/null +++ b/data/installed-tests/fwupd-tests.xml @@ -0,0 +1,77 @@ + + + + fakedevice.firmware + FakeDevice Firmware + Firmware for the ACME Corp Integrated Webcam + ACME Corp + GPL-2.0+ +

Updating the firmware on your webcam device improves performance and adds new features.

+ http://www.acme.com/ + + + 17 + 1163 + ./fakedevice124.cab + fc0aabcf98bf3546c91270f2941f0acd0395dd79 + 2b8546ba805ad10bf8a2e5ad539d53f303812ba5 +

Fixes another bug with the flux capacitor to prevent time going backwards.

+
+ + 17 + 1153 + ./fakedevice123.cab + bc3c32f42cf33fe5aade64f999417251fd8208d3 + 7998cd212721e068b2411135e1f90d0ad436d730 +

Fixes a bug with the flux capacitor to avoid year 2038 overflow.

+
+
+ + b585990a-003e-5270-89d5-3705a17f9a43 + +
+ + com.hughski.ColorHug2.firmware + ColorHug2 + Firmware for the Hughski ColorHug2 Colorimeter + Hughski Limited + GPL-2.0+ +

Updating the firmware on your ColorHug2 device improves performance and adds new features.

+ http://www.hughski.com/ + + + 16384 + 19592 + hughski-colorhug2-2.0.7.cab + 490be5c0b13ca4a3f169bf8bc682ba127b8f7b96 + 658851e6f27c4d87de19cd66b97b610d100efe09 +

This release fixes prevents the firmware returning an error when the remote SHA1 hash was never sent.

+
+
+ + 2082b5e0-7a64-478a-b1b2-e3404fab6dad + +
+ + com.hughski.ColorHug.firmware + ColorHug + Firmware for the Hughski ColorHug Colorimeter + Hughski Limited + GPL-2.0+ +

Updating the firmware on your ColorHug device improves performance and adds new features.

+ http://www.hughski.com/ + + + 16384 + 18054 + hughski-colorhug-1.2.6.cab + 570a4259af0c7670f3883e84d2f4e6ff7de572c2 + 111784ffadfd5dd43f05655b266b5142230195b6 +

This release fixes prevents the firmware returning an error when the remote SHA1 hash was never sent.

+
+
+ + 40338ceb-b966-4eae-adae-9c32edfcc484 + +
+
diff --git a/data/installed-tests/fwupdmgr.sh b/data/installed-tests/fwupdmgr.sh index 1df2efb49..e4ff45265 100755 --- a/data/installed-tests/fwupdmgr.sh +++ b/data/installed-tests/fwupdmgr.sh @@ -9,8 +9,8 @@ fwupdmgr get-remotes rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi # --- -echo "Refreshing with dummy metadata..." -fwupdmgr refresh ${dirname}/firmware-example.xml.gz ${dirname}/firmware-example.xml.gz.asc lvfs +echo "Enabling fwupd-tests remote..." +fwupdmgr enable-remote fwupd-tests rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi # --- diff --git a/data/installed-tests/meson.build b/data/installed-tests/meson.build index 4e38f95a5..eb33fa9f5 100644 --- a/data/installed-tests/meson.build +++ b/data/installed-tests/meson.build @@ -13,8 +13,7 @@ configure_file( install_data([ 'fwupdmgr.sh', - 'firmware-example.xml.gz', - 'firmware-example.xml.gz.asc', + 'fwupd-tests.xml', 'hardware.py', ], install_dir : 'share/installed-tests/fwupd', @@ -46,3 +45,12 @@ custom_target('installed-cab124', install: true, install_dir: join_paths('share', 'installed-tests', 'fwupd'), ) + +# replace @installedtestsdir@ +configure_file( + input : 'remote.conf.in', + output : 'fwupd-tests.conf', + configuration : con2, + install: true, + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), +) diff --git a/data/installed-tests/remote.conf.in b/data/installed-tests/remote.conf.in new file mode 100644 index 000000000..dca038b37 --- /dev/null +++ b/data/installed-tests/remote.conf.in @@ -0,0 +1,9 @@ +[fwupd Remote] +# This is a local fwupd remote that is used only for installed tests +# either from continuous integration or for fake devices from fwupd +# frontends + +Enabled=false +Title=fwupd test suite +Keyring=none +MetadataURI=file://@installedtestsdir@/fwupd-tests.xml diff --git a/data/meson.build b/data/meson.build index 4f7c9c4d7..67e2b0584 100644 --- a/data/meson.build +++ b/data/meson.build @@ -3,8 +3,10 @@ subdir('pki') subdir('remotes.d') subdir('bash-completion') -if get_option('tests') and get_option('daemon') +if get_option('tests') subdir('tests') +endif +if get_option('daemon') subdir('installed-tests') endif @@ -38,7 +40,7 @@ if get_option('systemd') rw_directories = [] rw_directories += join_paths (localstatedir, 'lib', 'fwupd') - rw_directories += join_paths (default_sysconfdir, 'fwupd', 'remotes.d') + rw_directories += join_paths (sysconfdir, 'fwupd', 'remotes.d') if get_option('plugin_uefi') rw_directories += ['-/boot/efi', '-/efi/EFI', '-/boot/EFI'] endif diff --git a/data/org.freedesktop.fwupd.metainfo.xml b/data/org.freedesktop.fwupd.metainfo.xml index 3a45c286d..bd99fe790 100644 --- a/data/org.freedesktop.fwupd.metainfo.xml +++ b/data/org.freedesktop.fwupd.metainfo.xml @@ -30,4 +30,1114 @@ moderate + + + +

This release adds the following features:

+
    +
  • Add a directory remote that generates metadata
  • +
  • Add a new remote type "directory"
  • +
  • Add a plugin to update Wacom embedded EMR and AES panels
  • +
  • Add a plugin to upgrade firmware on ATA-ATAPI hardware
  • +
  • Add a quirk to use the legacy bootmgr description
  • +
  • Add flag to support manually aligning the NVMe firmware to the FWUG value
  • +
  • Add SuperIO IT89xx device support
  • +
  • Add support for Dell dock passive flow
  • +
  • Add 'update' and 'get-updates' commands to fwupdtool
  • +
  • Allow Dell dock flashing Thunderbolt over I2C
  • +
  • Check the battery percentage before flashing
  • +
  • Show a per-release source and details URL
  • +
  • Show a `UpdateMessage` and display it in tools
  • +
+

This release fixes the following bugs:

+
    +
  • Add the needs-shutdown quirk to Phison NVMe drives
  • +
  • Correct Nitrokey Storage invalid firmware version read
  • +
  • Do not check the BGRT status before uploading a UX capsule
  • +
  • Do the UEFI UX checksum calculation in fwupd
  • +
  • Fix flashing various Jabra devices
  • +
  • Fix the parser to support extended segment addresses
  • +
  • Flash the fastboot partition after downloading the file
  • +
  • Show a console warning if loading an out-of-tree plugin
  • +
  • Support FGUID to get the SKU GUID for NVMe hardware
  • +
+
+
+ + +

This release fixes the following bug:

+
    +
  • Correctly migrate the history database
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add support for devices that support fastboot
  • +
  • Add more standard USB identifier GUIDs
  • +
  • Add new API to get the release protocol from the metadata
  • +
  • Add the PCR0 value as the device checksum for system firmware
  • +
  • Include the device firmware checksum and update protocol in the report
  • +
+

This release fixes the following bugs:

+
    +
  • Add Dell TB18DC to the supported devices list
  • +
  • Allow replacing the last byte in the image when using 'dfu-tool replace-data'
  • +
  • Append the UEFI capsule header in userspace rather than in the loader
  • +
  • Check the device checksum as well as the content checksum during verify
  • +
  • Correctly parse format the version numbers correctly using old metadata
  • +
  • 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
  • +
  • Sanitize the version if the version format has been specified
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add per-release install duration values
  • +
  • Shut down the daemon after 2h of inactivity when possible
  • +
+

This release fixes the following bugs:

+
    +
  • Fix a use-after-free when using --immediate-exit
  • +
  • Fix flashing the 8bitdo SF30
  • +
  • Fix showing the custom remote agreements
  • +
  • Include the os-release information in the release metadata
  • +
  • Speed up startup by loading less thunderbolt firmware
  • +
  • Speed up startup by using a silo index for GUID queries
  • +
  • Use less memory and fragment the heap less when starting
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a plugin for an upcoming Dell USB-C dock
  • +
  • Add a standalone installer creation script
  • +
  • Add support for devices to show an estimated flash time
  • +
  • Add support for some new Realtek USB devices
  • +
  • Allow firmware files to depend on versions from other devices
  • +
  • Allow setting the version format from a quirk entry
  • +
  • Port from libappstream-glib to libxmlb for a large reduction in RSS
  • +
  • Stop any running daemon over dbus when using fu-tool
  • +
  • Support the Intel ME version format
  • +
+

This release fixes the following bugs:

+
    +
  • Add version format quirks for several Lenovo machines
  • +
  • Adjust panamera ESM update routine for some reported issues
  • +
  • Adjust synapticsmst EVB board handling
  • +
  • Check the amount of free space on the ESP
  • +
  • Don't show devices pending a reboot in GetUpgrades
  • +
  • Ensure that parent ID is created before creating quirked children
  • +
  • Optionally wait for replug before updating a device
  • +
  • Set the full AMT device version including the BuildNum
  • +
  • Sort the firmware sack by component priority
  • +
  • Stop showing errors when no Dell dock plugged in
  • +
  • Stop showing the current release during updates in fwupdmgr
  • +
  • Update all sub-devices for a composite update
  • +
  • Use HTTPS_PROXY if set
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • + Add a new device flag 'ignore-validation' that will + override checks +
  • +
  • Add a new plugin to enumerate EC firmware
  • +
  • Add a new plugin to update NVMe hardware
  • +
  • Add a plugin for updating using the flashrom command line tool
  • +
  • Allow the device list to take care of waiting for the device replug
  • +
  • Allow updating just one specific device from the command line
  • +
  • Allow upgrades using a self-signed fwupd.efi binary
  • +
  • Download firmware if the user specifies a URI
  • +
  • Include serial number in daemon device output when trusted
  • +
  • Notify all plugins of device removals through a new vfunc
  • +
  • Use boltd force power API if available
  • +
+

This release fixes the following bugs:

+
    +
  • Add an install hook for classic snap
  • +
  • Allow forcing installation even if no AC power is applied
  • +
  • Allow using --force to ignore version_lowest
  • +
  • Always use the same HardwareIDs as Windows
  • +
  • Check the device state before assuming a fake DFU runtime
  • +
  • Copy over parent GUIDs from other plugin donors
  • +
  • Detect location of python3 interpreter
  • +
  • Do not add udev devices after a small delay
  • +
  • Don't fail to run if compiled without GPG/PKCS7
  • +
  • Fix a segfault in fwupdtool caused by cleanup of USB plugins
  • +
  • Implement the systemd recommendations for offline updates
  • +
  • Improve performance when reading keys from the quirk database
  • +
  • Remove children of devices when the parent is removed
  • +
  • Rewrite synapticsmst to use modern error handling
  • +
  • + Rewrite the unifying plugin to use the new daemon-provided + functionality +
  • +
  • Show a time estimate on the progressbar after an update has started
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add support for the Synaptics Panamera hardware
  • +
  • Add validation for Alpine and Titan Ridge
  • +
  • Improve the Redfish plugin to actually work with real hardware
  • +
+

This release fixes the following bugs:

+
    +
  • Allow different plugins to add the same device
  • +
  • Allow flashing unifying devices in recovery mode
  • +
  • Allow running synapticsmst on non-Dell hardware
  • +
  • Check the ESP for sanity at startup
  • +
  • Do not hold hidraw devices open forever
  • +
  • Don't override _FORTIFY_SOURCE when building the EFI binary
  • +
  • Don't show passwords in fwupdmgr
  • +
  • Fix a potential segfault in smbios data parsing
  • +
  • Fix encoding the GUID into the capsule EFI variable
  • +
  • Fix various bugs when reading the thunderbolt version number
  • +
  • Reboot synapticsmst devices at the end of flash cycle
  • +
  • Show status messages when the daemon is initializing
  • +
  • Show the correct title when updating devices
  • +
  • Show the reasons that plugins are not run on the CLI
  • +
  • Use localedir in po/make-images
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a initial Redfish support
  • +
  • Add a tool to mimic the original fwupdate CLI interface
  • +
  • Allow devices to assign a plugin from the quirk subsystem
  • +
  • Change the quirk file structure to be more efficient
  • +
  • Merge fwupdate functionality into fwupd
  • +
  • + Run a plugin vfunc before and after all the composite devices are + updated +
  • +
  • Support more Wacom tablets
  • +
+

This release fixes the following bugs:

+
    +
  • Add release information for locked devices
  • +
  • Allow building with older meson
  • +
  • Detect the EFI system partition location at runtime
  • +
  • Do not use 8bitdo bootloader commands after a successful flash
  • +
  • Enable accesing downloaded files in flatpak and snap
  • +
  • Fix a potential buffer overflow when applying a DFU patch
  • +
  • Fix downgrading older releases to devices
  • +
  • Fix flashing devices that require a manual replug
  • +
  • Fix several small memory leaks in various places
  • +
  • Fix the retrieval of Redfish version
  • +
  • Fix unifying failure to detach when using a slow host controller
  • +
  • Set the Wacom device status when erasing and writing firmware
  • +
  • Show errors in the CLI if unable to access directory
  • +
  • Use the parent device name for Wacom sub-modules
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a plugin to update some future Wacom tablets
  • +
  • Add 'fwupdmgr get-topology' to show logical device tree
  • +
  • Add support for creating a flatpak
  • +
  • Add support for creating a snap
  • +
  • Add support for Motorola S-record files
  • +
  • Add the Linux Foundation public GPG keys for firmware and metadata
  • +
  • Show a translated warning when the server is limiting downloads
  • +
+

This release fixes the following bugs:

+
    +
  • Add a firmware diagnostic tool called fwupdtool
  • +
  • Adjust all licensing to LGPL 2.1+
  • +
  • + Allow installing more than one firmware using 'fwupdmgr + install' +
  • +
  • Allow specifying hwids with OR relationships
  • +
  • Do not call fu_plugin_init() on blacklisted plugins
  • +
  • Do not require libcolorhug to build
  • +
  • Fix a crash in libfwupd where no device ID is set
  • +
  • Fix a potential DoS in libdfu by limiting holes to 1MiB
  • +
  • Fix a segfault that sometimes occurs during cleanup of USB plugins
  • +
  • Fix Hardware-ID{0,1,2,12} compatibility with Microsoft
  • +
  • Hide devices that aren't updatable by default in fwupdmgr
  • +
  • Search all UEFI GUIDs when matching hardware
  • +
  • Stop matching Nintendo Switch Pro in the 8bitdo plugin
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add enable-remote and disable-remote commands to fwupdmgr
  • +
  • Add fu_plugin_add_compile_version() for libraries to use
  • +
  • Allow requiring specific versions of libraries for firmware updates
  • +
  • If no remotes are enabled try to enable the LVFS
  • +
  • Show a warning with interactive prompt when enabling a remote
  • +
+

This release fixes the following bugs:

+
    +
  • Check that EFI system partition is mounted before update
  • +
  • Disable synapticsmst remote control on failure
  • +
  • Don't recoldplug thunderbolt to fix a flashing failure
  • +
  • Fix SQL error when running 'fwupdmgr clear-offline'
  • +
  • Improve the update report message
  • +
  • Only enumerate Dell Docks if the type is known
  • +
  • Only run certtool if a new enough gnutls is present
  • +
  • Prevent a client crash if the daemon somehow sends invalid data
  • +
  • Reboot after scheduling using logind not systemd
  • +
  • Use the right encoding for the label in make-images
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add bash completion for fwupdmgr
  • +
  • Add support for newest Thunderbolt chips
  • +
  • Allow all functions that take device arguments to be prompted
  • +
  • Allow devices to use the runtime version when in bootloader mode
  • +
  • Allow overriding ESP mount point via conf file
  • +
  • Delete any old fwupdate capsules and efivars when launching fwupd
  • +
  • Generate Vala bindings
  • +
+

This release fixes the following bugs:

+
    +
  • Allow ctrl-d out of the prompt for devices
  • +
  • Allow to create package out of provided binary
  • +
  • Correct handling of unknown Thunderbolt devices
  • +
  • Correctly detect new remotes that are manually copied
  • +
  • Fix a crash related to when passing device to downgrade in CLI
  • +
  • Fix running the self tests when no fwupd is installed
  • +
  • Fix Unifying signature writing and parsing for Texas bootloader
  • +
  • Only send success and failure reports to the server
  • +
  • Use a CNAME to redirect to the correct CDN for metadata
  • +
  • Use a longer timeout when powering back the Thunderbolt device
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Offer to reboot when processing an offline update
  • +
  • Report the efivar, libsmbios and fwupdate library versions
  • +
  • Report Thunderbolt safe mode and SecureBoot status
  • +
  • Show the user a URL when they report a known problem
  • +
  • Support split cabinet archives as produced by Windows Update
  • +
+

This release fixes the following bugs:

+
    +
  • Be more careful deleting and modifying device history
  • +
  • Clarify which devices don't have upgrades
  • +
  • Ensure the Thunderbolt version is xx.yy
  • +
  • Fix a daemon warning when using fwupdmgr get-results
  • +
  • Fix crasher with MST flashing
  • +
  • Fix DFU detach with newer releases of libusb
  • +
  • Include the device VID and PID when generating the device-id
  • +
  • Set the RemoteId when using GetDetails
  • +
  • Stop matching 8bitdo DS4 controller VID/PID
  • +
  • Use help2man for dfu-tool and drop docbook dependencies
  • +
  • Use ngettext for any strings with plurals
  • +
  • Use the default value if ArchiveSizeMax is unspecified
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add D-Bus methods to get and modify the history information
  • +
  • Allow the user to share firmware update success or failure
  • +
  • Ask the user to refresh metadata when it is very old
  • +
  • Store firmware update success and failure to a local database
  • +
+

This release fixes the following bugs:

+
    +
  • Add a device name for locked UEFI devices
  • +
  • Allow each plugin to opt-in to the recoldplug action
  • +
  • Fix firmware downloading using gnome-software
  • +
  • Fix UX capsule reference to the one specified in efivar
  • +
  • Never add two devices to the daemon with the same ID
  • +
  • Rescan supported flags when refreshing metadata
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a new plugin to add support for CSR 'Driverless DFU'
  • +
  • Add initial SF30/SN30 Pro support
  • +
  • Support AppStream metadata with relative <location> URLs
  • +
+

This release fixes the following bugs:

+
    +
  • Add more metadata to the user-agent string
  • +
  • Block owned Dell TPM updates
  • +
  • Choose the correct component from provides matches using requirements
  • +
  • Do not try to parse huge compressed archive files
  • +
  • Fix a double-free bug in the Udev code
  • +
  • Handle Thunderbolt 'native' mode
  • +
  • + Use the new functionality in libgcab >= 1.0 to avoid writing temp + files +
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a plugin for the Nitrokey Storage device
  • +
  • Add support for the original AVR DFU protocol
  • +
  • Allow different plugins to claim the same device
  • +
  • Allow quirks to set common USB properties
  • +
  • Move a common plugin functionality out to a new shared object
  • +
  • Optionally delay the device removal for better replugging
  • +
  • Set environment variables to allow easy per-plugin debugging
  • +
  • Use a SHA1 hash for the internal DeviceID
  • +
+

This release fixes the following bugs:

+
    +
  • Add quirk for AT32UC3B1256 as used in the RubberDucky
  • +
  • Disable the dell plugin if libsmbios fails
  • +
  • Don't register for USB UDev events to later ignore them
  • +
  • Fix a possible buffer overflow when debugging ebitdo devices
  • +
  • Fix critical warning when more than one remote fails to load
  • +
  • Fix DFU attaching AVR32 devices like the XMEGA
  • +
  • Ignore useless Thunderbolt device types
  • +
  • Refactor ColorHug into a much more modern plugin
  • +
  • Release the Steelseries interface if getting the version failed
  • +
  • Remove autoconf-isms from the meson configure options
  • +
  • Show a nicer error message if the requirement fails
  • +
  • Sort the output of GetUpgrades correctly
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add support for HWID requirements
  • +
  • Add support for programming various AVR32 and XMEGA parts using DFU
  • +
  • Add the various DFU quirks for the Jabra Speak devices
  • +
  • Allow specifying the output file type for 'dfu-tool read'
  • +
  • Move the database of supported devices out into runtime loaded files
  • +
  • Support the IHEX record type 0x05
  • +
  • Use help2man to generate the man page at build time
  • +
  • Use the new quirk infrastructure for version numbers
  • +
+

This release fixes the following bugs:

+
    +
  • Catch invalid Dell dock component requests
  • +
  • Correctly output Intel HEX files with > 16bit offset addresses
  • +
  • Do not try to verify the element write if upload is unsupported
  • +
  • Fix a double-unref when updating any 8Bitdo device
  • +
  • Fix crash when enumerating with Dell dock connected but with no UEFI
  • +
  • Fix uploading large firmware files over DFU
  • +
  • Format the BCD USB revision numbers correctly
  • +
  • Guess the DFU transfer size if it is not specified
  • +
  • Include the reset timeout as wValue to fix some DFU bootloaders
  • +
  • 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 + fwupdmgr +
  • +
  • Use the correct wDetachTimeOut when writing DFU firmware
  • +
  • Verify devices with legacy VIDs are actually 8Bitdo controllers
  • +
+
+
+ + +

This release breaks API and ABI to remove deprecated symbols!

+

This release adds the following features:

+
    +
  • Add a human-readable title for each remote
  • +
  • Add a method to return a list of upgrades for a specific device
  • +
  • + Add an 'Summary' and 'Icons' properties to each + device +
  • +
  • Add FuDeviceLocker to simplify device open/close lifecycles
  • +
  • Add functionality to blacklist Dell HW with problems
  • +
  • Add fu_plugin_check_supported()
  • +
  • Add fwupd_remote_get_checksum() to use in client programs
  • +
  • Add ModifyRemote as an easy way to enable and disable remotes
  • +
  • Add the plugin documentation to the main gtk-doc
  • +
  • Allow plugins to depend on each other
  • +
  • Disable the fallback USB plugin
  • +
  • Parse the SMBIOS v2 and v3 DMI tables directly
  • +
  • Support uploading the UEFI firmware splash image
  • +
  • Use the intel-wmi-thunderbolt kernel module to force power
  • +
+

This release fixes the following bugs:

+
    +
  • Only run SMI to toggle host MST GPIO on Dell systems with host MST
  • +
  • Disable unifying support if no CONFIG_HIDRAW support
  • +
  • Do not auto-open all USB devices at startup
  • +
  • Do not fail to load the daemon if cached metadata is invalid
  • +
  • Do not use system-specific infomation for UEFI PCI devices
  • +
  • Fix a crash when using fu_plugin_device_add_delay()
  • +
  • Fix the libdfu self test failure on s390 and ppc64
  • +
  • Fix various printing issues with the progressbar
  • +
  • Generate the LD script from the GObject introspection data
  • +
  • Never fallback to an offline update from client code
  • +
  • Only set the Dell coldplug delay when we know we need it
  • +
  • Prefer to use HWIDs to get DMI keys and DE table
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a configure switch for the LVFS remotes
  • +
  • Add a FirmwareBaseURI parameter to the remote config
  • +
  • Add a firmware builder that uses bubblewrap
  • +
  • + Add a python script to create fwupd compatible cab files from + Microsoft .exe files +
  • +
  • Add a thunderbolt plugin for new kernel interface
  • +
  • Allow plugins to get DMI data from the hardware in a safe way
  • +
  • Allow plugins to set metadata on devices created by other plugins
  • +
  • Optionally install the LVFS PKCS7 root certificate
  • +
  • Optionally use GnuTLS to verify PKCS7 certificates
  • +
+

This release fixes the following bugs:

+
    +
  • Add back options for HAVE_SYNAPTICS and HAVE_THUNDERBOLT
  • +
  • Allow configuring systemd and udev directories
  • +
  • Enable C99 support in meson.build
  • +
  • Fix an incomplete cipher when using XTEA on data not in 4 byte chunks
  • +
  • Fix minor const-correctness issues
  • +
  • Implement thunderbolt image validation
  • +
  • Remove the confusing ALLOW_OFFLINE and ALLOW_ONLINE flags
  • +
  • Show a bouncing progress bar if the percentage remains at zero
  • +
  • Use a hwid to match supported systems for synapticsmst
  • +
  • Use the new bootloader PIDs for Unifying pico receivers
  • +
  • When thunderbolt is in safe mode on a Dell recover using SMBIOS
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add DfuPatch to support forward-only firmware patching
  • +
  • Add --version option to fwupdmgr
  • +
  • Display all errors recorded by efi_error tracing
  • +
  • Make building introspection optional
  • +
  • Support embedded devices with local firmware metadata
  • +
+

This release fixes the following bugs:

+
    +
  • Check all the device GUIDs against the blacklist when added
  • +
  • Correct a memory leak in Dell plugin
  • +
  • Default to 'en' for UEFI capsule graphics
  • +
  • Don't log a warning when an unknown unifying report is parsed
  • +
  • Enable test suite via /etc/fwupd.conf
  • +
  • Fix a hang on 32 bit computers
  • +
  • Fix compilation of the policy on a variety of configurations
  • +
  • Fix UEFI crash when the product name is NULL
  • +
  • Make flashing ebitdo devices work with fu-ebitdo-tool
  • +
  • Make messages from installing capsules useful
  • +
  • Make sure the unifying percentage completion goes from 0% to 100%
  • +
  • Run the plugin coldplug methods in a predictable order
  • +
  • Test UEFI for kernel support during coldplug
  • +
  • Use new GUsb functionality to fix flashing Unifying devices
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a get-remotes command to fwupdmgr
  • +
  • Add a plugin to get the version of the AMT ME interface
  • +
  • Add Arch Linux to CI
  • +
  • Add some installed tests flashing actual hardware
  • +
  • Allow flashing Unifying devices in bootloader modes
  • +
  • Allow ordering the metadata remotes
  • +
+

This release fixes the following bugs:

+
    +
  • Do not check the runtime if the DFU device is in bootloader mode
  • +
  • Do not unlock devices when doing VerifyUpdate
  • +
  • Filter by Unifying SwId when making HID++2.0 requests
  • +
  • Fix downgrades when version_lowest is set
  • +
  • Fix the self tests when running on PPC64 big endian
  • +
  • Move the remotes parsing from the client to the server
  • +
  • Split up the Unifying HID++2.0 and HID++1.0 functionality
  • +
  • Store the metadata files rather than merging to one store
  • +
  • Use a longer timeout for some Unifying operations
  • +
  • Use the UFY DeviceID prefix for Unifying devides
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add installed tests that use the daemon
  • +
  • Add the ability to restrict firmware to specific vendors
  • +
  • Enable Travis CI for Fedora and Debian
  • +
  • Export some more API for dealing with checksums
  • +
  • Generate a images for status messages during system firmware update
  • +
  • Show progress download when refreshing metadata
  • +
+

This release fixes the following bugs:

+
    +
  • Compile with newer versions of meson
  • +
  • Ensure that firmware provides are legal GUIDs
  • +
  • Fix a common crash when refreshing metadata
  • +
  • Use the correct type signature in the D-Bus introspection file
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a 'downgrade' command to fwupdmgr
  • +
  • Add a 'get-releases' command to fwupdmgr
  • +
  • Add support for ConsoleKit2
  • +
  • Add support for Microsoft HardwareIDs
  • +
  • Allow downloading metadata from more than just the LVFS
  • +
  • Allow multiple checksums on devices and releases
  • +
+

This release fixes the following bugs:

+
    +
  • Allow to specify bindir
  • +
  • Correctly open Unifying devices with original factory firmware
  • +
  • Deprecate some of the old FwupdResult API
  • +
  • Do not copy the origin from the new metadata file
  • +
  • Do not expect a Unifying reply when issuing a REBOOT command
  • +
  • Do not re-download firmware that exists in the cache
  • +
  • Fix a problem when testing for a Dell system
  • +
  • Fix flashing new firmware to 8bitdo controllers
  • +
  • Increase minimum required AppStream-Glib version to 0.6.13
  • +
  • Make documentation and man pages optional
  • +
  • Make systemd dependency at least version 231
  • +
  • Only decompress the firmware after the signature check
  • +
  • Remove 'lib' prefix when looking for libraries
  • +
  • Return the remote ID when getting updates about hardware
  • +
  • Send the daemon the remote ID when sending firmware metadata
  • +
+
+
+ + +

This release adds the following feature:

+
    +
  • Add support for Unifying DFU features
  • +
+

This release fixes the following bugs:

+
    +
  • Do not spew a critial warning when parsing an invalid URI
  • +
  • Ensure device is closed if did not complete setup
  • +
  • Ensure steelseries device is closed if it returns an invalid packet
  • +
  • Fix man page installation location
  • +
  • Ignore spaces in the Unifying version prefix
  • +
  • Set HAVE_POLKIT_0_114 when polkit is newer than 0.114
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a config option to allow runtime disabling plugins by name
  • +
  • Add the Meson build system and remove autotools
  • +
  • Support signed Intel HEX files
  • +
+

This release fixes the following bugs:

+
    +
  • Add DFU quirk for OpenPICC and SIMtrace
  • +
  • Create directories in /var/cache as required
  • +
  • Refactor the unifying plugin now we know more about the hardware
  • +
  • Set the source origin when saving metadata
  • +
  • Support proxy servers in fwupdmgr
  • +
  • Use a 60 second timeout on all client downloads
  • +
+
+
+ + +

This release fixes the following bugs:

+
    +
  • Adjust systemd confinement restrictions
  • +
  • Do not hardcode docbook2man path
  • +
  • Don't initialize libsmbios on unsupported systems
  • +
  • Fix a crash when enumerating devices on a Dell WLD15
  • +
  • Fix compiler warnings
  • +
  • Fix fwupdmgr timeout with missing pending database
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a set of vfuncs that are run before and after a device update
  • +
  • + Add Dell-specific functionality to allow other plugins turn on + TBT/GPIO +
  • +
  • Add support for Intel Thunderbolt devices
  • +
  • Add support for Logitech Unifying devices
  • +
  • Add support for Synaptics MST cascades hubs
  • +
  • Add support for the Altus-Metrum ChaosKey device
  • +
  • Add VerifyUpdate to update the device checksums server-side
  • +
  • + Allow the metadata to match a version of fwupd and the existing fw + version +
  • +
+

This release fixes the following bugs:

+
    +
  • Add a new method for forcing a controller to flash mode
  • +
  • Always make sure we're getting a C99 compiler
  • +
  • Close USB devices before error returns
  • +
  • Don't read data from some DfuSe targets
  • +
  • Include all debug messages when run with --verbose
  • +
  • Return the pending UEFI update when not on AC power
  • +
  • + Use a heuristic for the start address if the firmware has no DfuSe + footer +
  • +
  • Use more restrictive settings when running under systemd
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a 'replace-data' command to dfu-tool
  • +
  • Use an animated progress bar when performing DFU operations
  • +
+

This release fixes the following bugs:

+
    +
  • Add quirks for HydraBus as it does not have a DFU runtime
  • +
  • + Don't create the UEFI dummy device if the unlock will happen on + next boot +
  • +
  • Enable hardening flags on more binaries
  • +
  • Fix an assert when unlocking the dummy ESRT device
  • +
  • Fix writing firmware to devices using the ST reference bootloader
  • +
  • Match the Dell TB16 device
  • +
  • Re-get the quirks when the DfuDevice gets a new GUsbDevice
  • +
  • Show the nicely formatted target name for DfuSe devices
  • +
  • Verify devices support updating in mode they are called
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add dfu_firmware_add_symbol()
  • +
  • Allow the argument to 'dfu-tool set-release' be major.minor
  • +
  • Load the Altos USB descriptor from ELF files
  • +
  • Support writing the IHEX symbol table
  • +
+

This release fixes the following bugs:

+
    +
  • Add a fallback for older appstream-glib releases
  • +
  • Fix a possible crash when uploading firmware files using libdfu
  • +
  • Fix libfwupd self tests when a host-provided fwupd is not available
  • +
  • + Show the human-readable version in the 'dfu-tool dump' + output +
  • +
  • Write the ELF files with the correct section type
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a set-address and set-target-size commands to dfu-util
  • +
  • Add a small library for talking with 0bitdo hardware
  • +
  • Add Dell TPM and TB15/WD15 support via new Dell provider
  • +
  • Add FU_DEVICE_FLAG_NEEDS_BOOTLOADER
  • +
  • Add fwupd_client_get_status()
  • +
  • Add fwupd_result_get_unique_id()
  • +
  • Add initial ELF reading and writing support to libdfu
  • +
  • Add support for installing multiple devices from a CAB file
  • +
  • Allow providers to export percentage completion
  • +
  • Show a progress notification when installing firmware
  • +
  • Show the vendor flashing instructions when installing
  • +
+

This release fixes the following bugs:

+
    +
  • Add XPS 9250 to Dell TPM modeswitch blacklist
  • +
  • Allow blacklisting devices by their GUID
  • +
  • Conditionally enable all providers based upon installed
  • +
  • Display flashes left in results output when it gets low
  • +
  • Do not attempt to add DFU devices not in runtime mode
  • +
  • Do not use the deprecated GNOME_COMPILE_WARNINGS
  • +
  • Don't fail while checking versions or locked state
  • +
  • Embed fwupd version in generated documentation
  • +
  • Ensure the ID is set when getting local firmware details
  • +
  • Fix gtk-doc build when srcdir != builddir
  • +
  • Fix libdfu hang when parsing corrupt IHEX files
  • +
  • Ignore devices that do not add at least one GUID
  • +
  • In get-details output, display the blob filename
  • +
  • Save the unique ID in the pending database
  • +
  • Support the 'DEVO' cipher kind in libdfu
  • +
  • Switch to the Amazon S3 CDN for firmware metadata
  • +
  • Update fwupdmgr manpage for new commands and arguments
  • +
  • Use a private gnupg key store
  • +
  • Use the correct firmware when installing a composite device
  • +
  • Use the SHA1 hash of the local file data as the origin
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a GetDetailsLocal() method to eventually replace GetDetails()
  • +
  • Add fu_device_get_alternate()
  • +
  • Allow devices to have multiple assigned GUIDs
  • +
  • Allow metainfo files to match only specific revisions of devices
  • +
  • Show the DFU protocol version in 'dfu-tool list'
  • +
+

This release fixes the following bugs:

+
    +
  • Enforce allowing providers to take away flash abilities
  • +
  • Only claim the DFU interface when required
  • +
  • Only return updatable devices from GetDevices()
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a --force flag to override provider warnings
  • +
  • Add device-added, device-removed and device-changed signals
  • +
  • Add dfu_image_get_element_default()
  • +
  • Add for a new device field 'Flashes Left'
  • +
  • Add fwupd_client_connect()
  • +
  • Add the 'monitor' debugging command for fwupdmgr
  • +
  • Add the 'supported' flag to the FuDevice
  • +
+

This release fixes the following bugs:

+
    +
  • Add summary and name field for Rival SteelSeries
  • +
  • Fix a critical warning when restarting the daemon
  • +
  • Fix BE issues when reading and writing DFU files
  • +
  • Make the device display name nicer
  • +
  • Match the AppStream metadata after a device has been added
  • +
  • Remove non-interactive pinentry setting from fu-keyring
  • +
  • Return all update descriptions newer than the installed version
  • +
  • Set the device description when parsing local firmware files
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a version plugin for SteelSeries hardware
  • +
  • Add FwupdClient and FwupdResult to libfwupd
  • +
  • Generate gtk-doc documentation for libfwupd
  • +
  • Return the device flags when getting firmware details
  • +
  • Support other checksum kinds
  • +
+

This release fixes the following bugs:

+
    +
  • Add Alienware to the version quirk table
  • +
  • Allow the test suite to run in %check
  • +
  • Do not return updates that require AC when on battery
  • +
  • Do not use /tmp for downloaded files
  • +
  • Test that GPG key import actually was successful
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add an unlock method for devices
  • +
  • Add a simple plugin infrastructure
  • +
  • Add ESRT enable method into UEFI provider
  • +
  • Install the hardcoded firmware AppStream file
  • +
+

This release fixes the following bugs:

+
    +
  • Correct the BCD version number for DFU 1.1
  • +
  • Do not use deprecated API from libappstream-glib
  • +
  • Ignore the DFU runtime on the DW1820A
  • +
  • Only read PCI OptionROM firmware when devices are manually unlocked
  • +
  • Require AC power before scheduling some types of firmware update
  • +
  • Show ignored DFU devices in dfu-util, but not in fwupd
  • +
+
+
+ + +

This release adds the following feature:

+
    +
  • + Add 'Created' and 'Modified' properties on + managed devices +
  • +
+

This release fixes the following bugs:

+
    +
  • Fix get-results for UEFI provider
  • +
  • Support vendor-specific UEFI version encodings
  • +
+
+
+ + +

This release fixes the following bugs:

+
    +
  • Always persist ColorHug devices after replug
  • +
  • Do not misdetect different ColorHug devices
  • +
  • Only dump the profiling data when run with --verbose
  • +
+
+
+ + +

+ This release adds a new GObject library called libdfu and a command + line client called dfu-tool. This is a low-level tool used to upgrade + USB device firmware and can either be shipped in the same package as + fwupd or split off as separate subpackages. +

+

This release adds the following feature:

+
    +
  • Add support for automatically updating USB DFU-capable devices
  • +
+

This release fixes the following bugs:

+
    +
  • Emit the changed signal after doing an update
  • +
  • Export the AppStream ID when returning device results
  • +
  • Fix compile with --disable-shared
  • +
  • Use new API available in fwup 0.5
  • +
  • Use the same device identification string format as Microsoft
  • +
+
+
+ + +

This release fixes the following bugs:

+
    +
  • Avoid seeking when reading the file magic during refresh
  • +
  • Do not assume that the compressed XML data will be NUL terminated
  • +
  • Use the correct user agent string for fwupdmgr
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add profiling data to debug slow startup times
  • +
  • Support cabinet archives files with more than one firmware
  • +
+

This release fixes the following bugs:

+
    +
  • Add the update description to the GetDetails results
  • +
  • + Clear the in-memory firmware store only after parsing a valid XML + file +
  • +
  • Ensure D-Bus remote errors are registered at fwupdmgr startup
  • +
  • + Fix verify-update to produce components with the correct provide + values +
  • +
  • Require appstream-glib 0.5.1
  • +
  • Show the dotted-decimal representation of the UEFI version number
  • +
  • + When the version is from the 'FW' extension do not cache + the device +
  • +
+
+
+ + +

This release fixes the following bugs:

+
    +
  • Fix the error message when no devices can be updated
  • +
  • Fix reading symlink to prevent crash with some compilers
  • +
+
+
+ + +

This release adds the following feature:

+
    +
  • Raise the dep on GLib to support and use g_autoptr()
  • +
+

This release fixes the following bugs:

+
    +
  • Do not merge existing firmware metadata
  • +
  • Do not reboot if racing with the PackageKit offline update mechanism
  • +
+
+
+ + +

This release adds the following feature:

+
    +
  • Remove fwsignd, we have the LVFS now
  • +
+

This release fixes the following bugs:

+
    +
  • Add application metadata when getting the updates list
  • +
  • Depend on appstream-glib >= 0.5.0
  • +
  • Don't apply firmware if something else is processing the update
  • +
  • Install fwupd into /usr/lib/$(triplet)/fwupd instead
  • +
  • Simplify the version properties on devices to avoid complexity
  • +
  • Update the offline update service to invoke right command
  • +
  • Use the new secure metadata URI
  • +
+
+
+ + +

+ For the device verification code to work correctly you need at least + libappstream-glib 0.5.0 installed. +

+

This release adds the following features:

+
    +
  • Add a Raspberry Pi firmware provider
  • +
  • Add a simple config file to store the correct LVFS download URI
  • +
  • Make parsing the option ROM runtime optional
  • +
+

This release fixes the following bugs:

+
    +
  • Allow fwupd to be autostarted by systemd
  • +
  • + Allow no arguments to 'fwupdmgr verify-update' and use sane + defaults +
  • +
  • Devices with option ROM are always internal
  • +
  • Do not pre-convert the update description from AppStream XML
  • +
  • Fix validation of written firmware
  • +
  • Move the verification and metadata matching phase to the daemon
  • +
  • Sign the test binary with the correct key
  • +
  • Use the AppStream 0.9 firmware specification by default
  • +
+
+
+ + +

+ In this release we've moved the LVFS website to the fwupd project + and made them work really well together. To update all the firmware on + your system is now just a case of 'fwupdmgr refresh && + fwupdmgr update'. + We've also added verification of BIOS and PCI ROM firmware, which + may be useful for forensics or to verify that system updates have been + applied. +

+

This release adds the following features:

+
    +
  • Actually parse the complete PCI option ROM
  • +
  • + Add a 'fwupdmgr update' command to update all devices to + latest versions +
  • +
  • Add a simple signing server that operates on .cab files
  • +
  • + Add a 'verify' command that verifies the cryptographic hash + of device firmware +
  • +
  • Allow clients to add new firmware metadata to the system cache
  • +
  • Move GetUpdates to the daemon
  • +
  • Move the LVFS website to the fwupd project
  • +
+

This release fixes the following bugs:

+
    +
  • Accept multiple files at one time when using fwupdmgr dump-rom
  • +
  • Automatically download metadata using fwupdmgr if required
  • +
  • Do not return NULL as a gboolean
  • +
  • Don't call efibootmgr after fwupdate
  • +
  • Fallback to offline install when calling the update argument
  • +
  • Fix Intel VBIOS detection on Dell hardware
  • +
  • Reload appstream data after refreshing
  • +
  • Use the new LVFS GPG key
  • +
  • Fix build: libgusb is required even without colorhug support
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Get the firmware version from the device descriptors
  • +
  • Run the offline actions using systemd when required
  • +
  • Support OpenHardware devices using the fwupd vendor extensions
  • +
+

This release fixes the following bugs:

+
    +
  • Add an UNKNOWN status so we can return meaningful enum values
  • +
  • Coldplug the devices before acquiring the well known name
  • +
+
+
+ + + + + + +

This release adds the following features:

+
    +
  • Add a 'get-updates' command to fwupdmgr
  • +
  • Add and document the offline-update lifecycle
  • +
  • Create a libfwupd shared library
  • +
+

This release fixes the following bugs:

+
    +
  • Create runtime directories if they do not exist
  • +
  • Do not crash when there are no devices to return
  • +
+
+
+ + +

fwupd is a simple daemon to allow session software to update firmware.

+
+
+
diff --git a/data/remotes.d/README.md b/data/remotes.d/README.md index d75e34d20..3a2791c2a 100644 --- a/data/remotes.d/README.md +++ b/data/remotes.d/README.md @@ -1,12 +1,12 @@ Vendor Firmware =============== -These are the steps to add vendor that is installed as part of an OSTree image: +These are the steps to add vendor firmware that is installed as part of an embedded image such as an OSTree or ChromeOS image: * Change `/etc/fwupd/remotes.d/vendor.conf` to have `Enabled=true` * Change `/etc/fwupd/remotes.d/vendor.conf` to have the correct `Title` * Deploy the firmware to `/usr/share/fwupd/remotes.d/vendor/firmware` -* Deploy the metadata to `/usr/share/fwupd/remotes.d/vendor/vendor.xml` +* Deploy the metadata to `/usr/share/fwupd/remotes.d/vendor/vendor.xml.gz` The metadata should be of the form: @@ -39,6 +39,20 @@ certificate. If this is the case also change `Keyring=gpg` or `Keyring=pkcs7` in `/etc/fwupd/remotes.d/vendor.conf` and ensure the correct public key or signing certificate is installed in the `/etc/pki/fwupd` location. +Automatic metadata generation +============================= +`fwupd` and `fwupdtool` support automatically generating metadata for a remote +by configuring it to be a *directory* type. This is very convenient if you want to dynamically add firmware from multiple packages while generating the image but there are a few deficiencies: +* There will be a performance impact of starting the daemon or tool measured by O(# CAB files) +* It's not possible to verify metadata signature and any file validation should be part of the image validation. + +To enable this: +* Change `/etc/fwupd/remotes.d/vendor-directory.conf` to have `Enabled=true` +* Change `/etc/fwupd/remotes.d/vendor.conf-directory` to have the correct `Title` +* Deploy the firmware to `/usr/share/fwupd/remotes.d/vendor/firmware` +* Change `MetadataURI` to that of the directory (Eg `/usr/share/fwupd/remotes.d/vendor/`) + + Mirroring a Repository ====================== diff --git a/data/remotes.d/meson.build b/data/remotes.d/meson.build index 824291fc5..2e6b499db 100644 --- a/data/remotes.d/meson.build +++ b/data/remotes.d/meson.build @@ -46,3 +46,10 @@ configure_file( install: true, install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) +configure_file( + input : 'vendor-directory.conf', + output : 'vendor-directory.conf', + configuration : con2, + install: true, + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), +) diff --git a/data/remotes.d/vendor-directory.conf b/data/remotes.d/vendor-directory.conf new file mode 100644 index 000000000..4a3f0eefc --- /dev/null +++ b/data/remotes.d/vendor-directory.conf @@ -0,0 +1,7 @@ +[fwupd Remote] +# this remote provides dynamically generated metadata shipped by the OS vendor and can +# be found in @datadir@/fwupd/remotes.d/vendor/firmware +Enabled=false +Title=Vendor (Automatic) +Keyring=none +MetadataURI=file://@datadir@/fwupd/remotes.d/vendor/firmware diff --git a/data/remotes.d/vendor.conf b/data/remotes.d/vendor.conf index 6cdf59cb7..e538b9722 100644 --- a/data/remotes.d/vendor.conf +++ b/data/remotes.d/vendor.conf @@ -1,7 +1,6 @@ [fwupd Remote] - # this remote provides metadata shipped by the OS vendor and can be found in -# /usr/share/fwupd/remotes.d/vendor and /usr/share/fwupd/remotes.d/vendor/firmware +# @datadir@/fwupd/remotes.d/vendor and firmware in @datadir@/fwupd/remotes.d/vendor/firmware Enabled=false Title=Vendor Keyring=none diff --git a/data/tests/remotes.d/directory.conf b/data/tests/remotes.d/directory.conf new file mode 100644 index 000000000..15feedb51 --- /dev/null +++ b/data/tests/remotes.d/directory.conf @@ -0,0 +1,4 @@ +[fwupd Remote] +Enabled=true +Keyring=none +MetadataURI=file:///tmp/fwupd-self-test/var/cache/fwupd diff --git a/debian/control.in b/debian/control.in index 47b8943d8..94c1cdc3d 100644 --- a/debian/control.in +++ b/debian/control.in @@ -22,8 +22,7 @@ Description: Firmware update daemon library fwupd is a daemon to allow session software to update device firmware. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. - Currently, firmware updates using the UEFI capsule format and for the - ColorHug are supported. More formats may be supported in the future. + Firmware updates are supported for a variety of technologies. See for details . This package provides the library used by the daemon. @@ -34,6 +33,8 @@ Depends: ${misc:Depends}, ${shlibs:Depends} Recommends: python3, bolt, + tpm2-tools, + tpm2-abrmd, fwupd-signed Breaks: gir1.2-dfu-1.0 (<< 0.9.7-1), libdfu1 (<< 0.9.7-1), @@ -46,8 +47,7 @@ Description: Firmware update daemon fwupd is a daemon to allow session software to update device firmware. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. - Currently, firmware updates using the UEFI capsule format and for the - ColorHug are supported. More formats may be supported in the future. + Firmware updates are supported for a variety of technologies. See for details Package: fwupd-tests @@ -69,8 +69,7 @@ Description: Test suite for firmware update daemon fwupd is a daemon to allow session software to update device firmware. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. - Currently, firmware updates using the UEFI capsule format and for the - ColorHug are supported. More formats may be supported in the future. + Firmware updates are supported for a variety of technologies. See for details . This package provides a set of installed tests that can be run to validate @@ -85,8 +84,7 @@ Description: Firmware update daemon documentation (HTML format) fwupd is a daemon to allow session software to update device firmware. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. - Currently, firmware updates using the UEFI capsule format and for the - ColorHug are supported. More formats may be supported in the future. + Firmware updates are supported for a variety of technologies. See for details . This package provides development documentation for creating a package that @@ -105,8 +103,7 @@ Description: development files for libfwupd fwupd is a daemon to allow session software to update device firmware. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. - Currently, firmware updates using the UEFI capsule format and for the - ColorHug are supported. More formats may be supported in the future. + Firmware updates are supported for a variety of technologies. See for details . This package provides the development files for libfwupd diff --git a/debian/docs b/debian/docs index edc007104..8b1378917 100644 --- a/debian/docs +++ b/debian/docs @@ -1 +1 @@ -NEWS + diff --git a/debian/fwupd-tests.install b/debian/fwupd-tests.install index 481df00f2..9002c417e 100644 --- a/debian/fwupd-tests.install +++ b/debian/fwupd-tests.install @@ -5,3 +5,4 @@ usr/share/installed-tests/* usr/lib/*/fwupd-plugins-3/libfu_plugin_test.so debian/lintian/fwupd-tests usr/share/lintian/overrides +etc/fwupd/remotes.d/fwupd-tests.conf diff --git a/debian/fwupd-tests.postinst b/debian/fwupd-tests.postinst index a3def5ecd..096c50f21 100644 --- a/debian/fwupd-tests.postinst +++ b/debian/fwupd-tests.postinst @@ -12,4 +12,12 @@ if [ "$1" = configure ] && [ -z "$2" ]; then echo "To enable test suite, modify /etc/fwupd/daemon.conf" fi fi + if [ -f /etc/fwupd/remotes.d/fwupd-tests.conf ]; then + if [ "$CI" = "true" ]; then + sed "s,^Enabled=false,Enabled=true," -i /etc/fwupd/remotes.d/fwupd-tests.conf + else + echo "To enable test suite, enable fwupd-tests remote" + fi + + fi fi diff --git a/debian/gir1.2-fwupd-2.0.install b/debian/gir1.2-fwupd-2.0.install index 78a92bbc2..eee37f27b 100644 --- a/debian/gir1.2-fwupd-2.0.install +++ b/debian/gir1.2-fwupd-2.0.install @@ -1 +1 @@ -usr/lib/*/girepository-1.0/Fwupd-2.0.typelib +usr/lib/*/girepository-1.0/*.typelib diff --git a/debian/libfwupd-dev.install b/debian/libfwupd-dev.install index dbc3671c9..8691785f0 100644 --- a/debian/libfwupd-dev.install +++ b/debian/libfwupd-dev.install @@ -1,6 +1,5 @@ -usr/include/fwupd-1/fwupd.h -usr/include/fwupd-1/libfwupd -usr/lib/*/libfwupd*.so -usr/lib/*/pkgconfig/fwupd.pc -usr/share/gir-1.0/Fwupd*.gir +usr/include/* +usr/lib/*/*.so +usr/lib/*/pkgconfig/*.pc +usr/share/gir-1.0/*.gir usr/share/vala/vapi diff --git a/debian/libfwupd2.install b/debian/libfwupd2.install index 6467ff9e0..3de3b10a4 100644 --- a/debian/libfwupd2.install +++ b/debian/libfwupd2.install @@ -1 +1 @@ -usr/lib/*/libfwup*.so.* +usr/lib/*/*.so.* diff --git a/debian/rules b/debian/rules index f29ee7934..96917917f 100755 --- a/debian/rules +++ b/debian/rules @@ -10,7 +10,8 @@ ifeq "$(DEB_HOST_ARCH_BITS)" "32" endif ifneq ($(CI),) - export CI=--werror + export CI=--werror --wrap-mode=forcefallback + export DHSLIBS=-- --ignore-missing-info endif regenerate_control: @@ -57,10 +58,15 @@ override_dh_install: dh_install -pfwupd usr/lib/fwupd/efi ;\ dh_install -pfwupd usr/lib/fwupd/fwupdate; \ fi + #if build with meson subproject in CI need to install this too + if [ -n "$CI" ] && [ -f debian/tmp/usr/lib/xb-tool ]; then \ + dh_install -pfwupd usr/lib/xb-tool ;\ + fi dh_missing -a --fail-missing #this is placed in fwupd-tests rm -f debian/fwupd/usr/lib/*/fwupd-plugins-3/libfu_plugin_test.so + rm -f debian/fwupd/etc/fwupd/remotes.d/fwupd-tests.conf ifeq (debian,$(SB_STYLE)) # Generate the template source for the Debian signing service to use @@ -95,3 +101,6 @@ ifeq (ubuntu,$(SB_STYLE)) dpkg-distaddfile $(tar_name) raw-uefi - ;\ fi endif + +override_dh_shlibdeps: + dh_shlibdeps $$DHSLIBS diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides index 1e5cac9aa..e13407f37 100644 --- a/debian/source/lintian-overrides +++ b/debian/source/lintian-overrides @@ -1,2 +1,4 @@ #github doesn't have these fwupd source: debian-watch-does-not-check-gpg-signature +#to make CI happy until libxmlb lands +fwupd source: source-is-missing diff --git a/docs/version-format.md b/docs/version-format.md new file mode 100644 index 000000000..40b3a8464 --- /dev/null +++ b/docs/version-format.md @@ -0,0 +1,58 @@ +Version Formats +=============== + +In some circumstances fwupd has to convert from a unsigned integer version +number into something that has either been used in documentation or has been +defined in some specification. +A good example here is the UEFI ESRT table, which specifies a `uint32_t` for +the version but does not specify how this should be formatted for the user. + +As is typical in underspecified specifications, vendors have converted the +integer in different ways. For instance, Dell uses version strings like 1.2.3 +and Microsoft use versions like 1.2.3.4. + +The fwudp daemon can match specific devices and apply the correct version style +using quirk files. The version format can also be specified in the firmware +`metainfo.xml` file so that the new version is correctly shown, and so that it +matches on the LVFS website. + +The current version formats supported by fwupd and the LVFS are: + + * `plain`: Use plain integer version numbers with no dots, e.g. `AABBCCDD` + * `quad`: Use Dell-style `AA.BB.CC.DD` version numbers + * `triplet`: Use Microsoft-style `AA.BB.CCDD` version numbers + * `pair`: Use two `AABB.CCDD` version numbers + * `bcd`: Use binary coded decimal notation + * `intel-me`: Use Intel ME-style notation (`aaa+11.bbbbb.CC.DDDD`) + * `intel-me2`: Use alternate Intel ME-style-style `A.B.CC.DDDD` notation + +These can be specified in quirk files like this: + + # Vendor Modelname + [Guid=5b92717b-2cad-4a96-a13b-9d65781df8bf] + VersionFormat = intel-me2 + +...or in metainfo.xml files like this: + + + intel-me2 + + +Runtime requirements +-------------------- + +Versions of fwupd `< 1.2.0` can only support firmware updates with key values +`LVFS::VersionFormat` of `quad` and `triplet`. Additionally, on older versions +no quirk `VersionFormat` device fixups are supported. + +If want to use one of the additional version formats you should depend on a +specific version of fwupd in the firmware file: + + + org.freedesktop.fwupd + + +This is not *strictly* required, as the integer value can be used for update +calculations if the version is specified in hex (e.g. `0x12345678`) in the +`` tag, although the user might get a bit confused if the update +version does not match the update description. diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c index b079fe800..649c979c8 100644 --- a/libfwupd/fwupd-client.c +++ b/libfwupd/fwupd-client.c @@ -37,6 +37,7 @@ static void fwupd_client_finalize (GObject *object); typedef struct { FwupdStatus status; + gboolean tainted; guint percentage; gchar *daemon_version; GDBusConnection *conn; @@ -57,6 +58,7 @@ enum { PROP_STATUS, PROP_PERCENTAGE, PROP_DAEMON_VERSION, + PROP_TAINTED, PROP_LAST }; @@ -131,6 +133,14 @@ fwupd_client_properties_changed_cb (GDBusProxy *proxy, g_object_notify (G_OBJECT (client), "status"); } } + if (g_variant_dict_contains (dict, "Tainted")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property (proxy, "Tainted"); + if (val != NULL) { + priv->tainted = g_variant_get_boolean (val); + g_object_notify (G_OBJECT (client), "tainted"); + } + } if (g_variant_dict_contains (dict, "Percentage")) { g_autoptr(GVariant) val = NULL; val = g_dbus_proxy_get_cached_property (proxy, "Percentage"); @@ -203,6 +213,7 @@ fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **e { FwupdClientPrivate *priv = GET_PRIVATE (client); g_autoptr(GVariant) val = NULL; + g_autoptr(GVariant) val2 = NULL; g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); @@ -235,6 +246,9 @@ fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **e val = g_dbus_proxy_get_cached_property (priv->proxy, "DaemonVersion"); if (val != NULL) fwupd_client_set_daemon_version (client, g_variant_get_string (val, NULL)); + val2 = g_dbus_proxy_get_cached_property (priv->proxy, "Tainted"); + if (val2 != NULL) + priv->tainted = g_variant_get_boolean (val2); return TRUE; } @@ -1138,6 +1152,24 @@ fwupd_client_get_status (FwupdClient *client) return priv->status; } +/** + * fwupd_client_get_tainted: + * @client: A #FwupdClient + * + * Gets if the daemon has been tainted by 3rd party code. + * + * Returns: %TRUE if the daemon is unsupported + * + * Since: 1.2.4 + **/ +gboolean +fwupd_client_get_tainted (FwupdClient *client) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + return priv->tainted; +} + /** * fwupd_client_update_metadata: * @client: A #FwupdClient @@ -1461,6 +1493,9 @@ fwupd_client_get_property (GObject *object, guint prop_id, case PROP_STATUS: g_value_set_uint (value, priv->status); break; + case PROP_TAINTED: + g_value_set_boolean (value, priv->tainted); + break; case PROP_PERCENTAGE: g_value_set_uint (value, priv->percentage); break; @@ -1595,9 +1630,20 @@ fwupd_client_class_init (FwupdClientClass *klass) */ pspec = g_param_spec_uint ("status", NULL, NULL, 0, FWUPD_STATUS_LAST, FWUPD_STATUS_UNKNOWN, - G_PARAM_READWRITE); + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_STATUS, pspec); + /** + * FwupdClient:tainted: + * + * If the daemon is tainted by 3rd party code. + * + * Since: 1.2.4 + */ + pspec = g_param_spec_boolean ("tainted", NULL, NULL, FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_TAINTED, pspec); + /** * FwupdClient:percentage: * @@ -1607,7 +1653,7 @@ fwupd_client_class_init (FwupdClientClass *klass) */ pspec = g_param_spec_uint ("percentage", NULL, NULL, 0, 100, 0, - G_PARAM_READWRITE); + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_PERCENTAGE, pspec); /** @@ -1618,7 +1664,7 @@ fwupd_client_class_init (FwupdClientClass *klass) * Since: 0.9.6 */ pspec = g_param_spec_string ("daemon-version", NULL, NULL, - NULL, G_PARAM_READABLE); + NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_DAEMON_VERSION, pspec); } diff --git a/libfwupd/fwupd-client.h b/libfwupd/fwupd-client.h index f33011faf..29bc13d54 100644 --- a/libfwupd/fwupd-client.h +++ b/libfwupd/fwupd-client.h @@ -116,6 +116,7 @@ gboolean fwupd_client_modify_device (FwupdClient *client, GCancellable *cancellable, GError **error); FwupdStatus fwupd_client_get_status (FwupdClient *client); +gboolean fwupd_client_get_tainted (FwupdClient *client); guint fwupd_client_get_percentage (FwupdClient *client); const gchar *fwupd_client_get_daemon_version (FwupdClient *client); diff --git a/libfwupd/fwupd-common.c b/libfwupd/fwupd-common.c index e2933a1e4..a32fbac90 100644 --- a/libfwupd/fwupd-common.c +++ b/libfwupd/fwupd-common.c @@ -45,7 +45,7 @@ fwupd_checksum_guess_kind (const gchar *checksum) } static const gchar * -_g_checksum_type_to_string (GChecksumType checksum_type) +fwupd_checksum_type_to_string_display (GChecksumType checksum_type) { if (checksum_type == G_CHECKSUM_MD5) return "MD5"; @@ -72,7 +72,9 @@ gchar * fwupd_checksum_format_for_display (const gchar *checksum) { GChecksumType kind = fwupd_checksum_guess_kind (checksum); - return g_strdup_printf ("%s(%s)", _g_checksum_type_to_string (kind), checksum); + return g_strdup_printf ("%s(%s)", + fwupd_checksum_type_to_string_display (kind), + checksum); } /** @@ -350,12 +352,27 @@ fwupd_build_history_report_json_device (JsonBuilder *builder, FwupdDevice *dev) { FwupdRelease *rel = fwupd_device_get_release_default (dev); GPtrArray *checksums; + const gchar *tmp; /* identify the firmware used */ json_builder_set_member_name (builder, "Checksum"); checksums = fwupd_release_get_checksums (rel); json_builder_add_string_value (builder, fwupd_checksum_get_by_kind (checksums, G_CHECKSUM_SHA1)); + /* identify the firmware written */ + checksums = fwupd_device_get_checksums (dev); + tmp = fwupd_checksum_get_by_kind (checksums, G_CHECKSUM_SHA1); + if (tmp != NULL) { + json_builder_set_member_name (builder, "ChecksumDevice"); + json_builder_add_string_value (builder, tmp); + } + + /* include the protocol used */ + if (fwupd_release_get_protocol (rel) != NULL) { + json_builder_set_member_name (builder, "Protocol"); + json_builder_add_string_value (builder, fwupd_release_get_protocol (rel)); + } + /* set the error state of the report */ json_builder_set_member_name (builder, "UpdateState"); json_builder_add_int_value (builder, fwupd_device_get_update_state (dev)); @@ -363,6 +380,10 @@ fwupd_build_history_report_json_device (JsonBuilder *builder, FwupdDevice *dev) json_builder_set_member_name (builder, "UpdateError"); json_builder_add_string_value (builder, fwupd_device_get_update_error (dev)); } + if (fwupd_release_get_update_message (rel) != NULL) { + json_builder_set_member_name (builder, "UpdateMessage"); + json_builder_add_string_value (builder, fwupd_release_get_update_message (rel)); + } /* map back to the dev type on the LVFS */ json_builder_set_member_name (builder, "Guid"); diff --git a/libfwupd/fwupd-device.c b/libfwupd/fwupd-device.c index f8972d29e..324a77477 100644 --- a/libfwupd/fwupd-device.c +++ b/libfwupd/fwupd-device.c @@ -51,6 +51,7 @@ typedef struct { guint32 install_duration; FwupdUpdateState update_state; gchar *update_error; + gchar *update_message; GPtrArray *releases; FwupdDevice *parent; } FwupdDevicePrivate; @@ -977,6 +978,8 @@ fwupd_device_incorporate (FwupdDevice *self, FwupdDevice *donor) fwupd_device_set_plugin (self, priv_donor->plugin); if (priv->update_error == NULL) fwupd_device_set_update_error (self, priv_donor->update_error); + if (priv->update_message == NULL) + fwupd_device_set_update_message (self, priv_donor->update_message); if (priv->version == NULL) fwupd_device_set_version (self, priv_donor->version); if (priv->version_lowest == NULL) @@ -1133,6 +1136,11 @@ fwupd_device_to_variant_full (FwupdDevice *device, FwupdDeviceFlags flags) FWUPD_RESULT_KEY_UPDATE_ERROR, g_variant_new_string (priv->update_error)); } + if (priv->update_message != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_UPDATE_MESSAGE, + g_variant_new_string (priv->update_message)); + } if (priv->update_state != FWUPD_UPDATE_STATE_UNKNOWN) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_UPDATE_STATE, @@ -1287,6 +1295,10 @@ fwupd_device_from_key_value (FwupdDevice *device, const gchar *key, GVariant *va fwupd_device_set_update_error (device, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_UPDATE_MESSAGE) == 0) { + fwupd_device_set_update_message (device, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_UPDATE_STATE) == 0) { fwupd_device_set_update_state (device, g_variant_get_uint32 (value)); return; @@ -1385,6 +1397,42 @@ fwupd_device_set_update_state (FwupdDevice *device, FwupdUpdateState update_stat priv->update_state = update_state; } +/** + * fwupd_device_get_update_message: + * @device: A #FwupdDevice + * + * Gets the update message. + * + * Returns: the update message, or %NULL if unset + * + * Since: 1.2.4 + **/ +const gchar * +fwupd_device_get_update_message (FwupdDevice *device) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); + return priv->update_message; +} + +/** + * fwupd_device_set_update_message: + * @device: A #FwupdDevice + * @update_message: the update message string + * + * Sets the update message. + * + * Since: 1.2.4 + **/ +void +fwupd_device_set_update_message (FwupdDevice *device, const gchar *update_message) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FWUPD_IS_DEVICE (device)); + g_free (priv->update_message); + priv->update_message = g_strdup (update_message); +} + /** * fwupd_device_get_update_error: * @device: A #FwupdDevice @@ -1545,6 +1593,7 @@ fwupd_device_to_string (FwupdDevice *device) fwupd_pad_kv_unx (str, FWUPD_RESULT_KEY_MODIFIED, priv->modified); fwupd_pad_kv_ups (str, FWUPD_RESULT_KEY_UPDATE_STATE, priv->update_state); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_UPDATE_ERROR, priv->update_error); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_UPDATE_MESSAGE, priv->update_message); for (guint i = 0; i < priv->releases->len; i++) { FwupdRelease *release = g_ptr_array_index (priv->releases, i); g_autofree gchar *tmp = fwupd_release_to_string (release); @@ -1590,6 +1639,7 @@ fwupd_device_finalize (GObject *object) g_free (priv->vendor_id); g_free (priv->plugin); g_free (priv->update_error); + g_free (priv->update_message); g_free (priv->version); g_free (priv->version_lowest); g_free (priv->version_bootloader); diff --git a/libfwupd/fwupd-device.h b/libfwupd/fwupd-device.h index 8b79a8001..63061e999 100644 --- a/libfwupd/fwupd-device.h +++ b/libfwupd/fwupd-device.h @@ -112,6 +112,9 @@ void fwupd_device_set_update_state (FwupdDevice *device, const gchar *fwupd_device_get_update_error (FwupdDevice *device); void fwupd_device_set_update_error (FwupdDevice *device, const gchar *update_error); +const gchar *fwupd_device_get_update_message (FwupdDevice *device); +void fwupd_device_set_update_message (FwupdDevice *device, + const gchar *update_message); void fwupd_device_add_release (FwupdDevice *device, FwupdRelease *release); GPtrArray *fwupd_device_get_releases (FwupdDevice *device); diff --git a/libfwupd/fwupd-enums-private.h b/libfwupd/fwupd-enums-private.h index 3eb6eb69c..02b2d3d61 100644 --- a/libfwupd/fwupd-enums-private.h +++ b/libfwupd/fwupd-enums-private.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2017 Richard Hughes + * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -14,11 +14,14 @@ #define FWUPD_RESULT_KEY_DEVICE_ID "DeviceId" /* s */ #define FWUPD_RESULT_KEY_PARENT_DEVICE_ID "ParentDeviceId"/* s */ #define FWUPD_RESULT_KEY_FILENAME "Filename" /* s */ +#define FWUPD_RESULT_KEY_PROTOCOL "Protocol" /* s */ #define FWUPD_RESULT_KEY_FLAGS "Flags" /* t */ #define FWUPD_RESULT_KEY_FLASHES_LEFT "FlashesLeft" /* u */ #define FWUPD_RESULT_KEY_INSTALL_DURATION "InstallDuration" /* u */ #define FWUPD_RESULT_KEY_GUID "Guid" /* as */ #define FWUPD_RESULT_KEY_HOMEPAGE "Homepage" /* s */ +#define FWUPD_RESULT_KEY_DETAILS_URL "DetailsUrl" /* s */ +#define FWUPD_RESULT_KEY_SOURCE_URL "SourceUrl" /* s */ #define FWUPD_RESULT_KEY_ICON "Icon" /* as */ #define FWUPD_RESULT_KEY_LICENSE "License" /* s */ #define FWUPD_RESULT_KEY_MODIFIED "Modified" /* t */ @@ -31,6 +34,7 @@ #define FWUPD_RESULT_KEY_SIZE "Size" /* t */ #define FWUPD_RESULT_KEY_SUMMARY "Summary" /* s */ #define FWUPD_RESULT_KEY_TRUST_FLAGS "TrustFlags" /* t */ +#define FWUPD_RESULT_KEY_UPDATE_MESSAGE "UpdateMessage" /* s */ #define FWUPD_RESULT_KEY_UPDATE_ERROR "UpdateError" /* s */ #define FWUPD_RESULT_KEY_UPDATE_STATE "UpdateState" /* u */ #define FWUPD_RESULT_KEY_URI "Uri" /* s */ diff --git a/libfwupd/fwupd-enums.c b/libfwupd/fwupd-enums.c index 6475159b6..9041710ca 100644 --- a/libfwupd/fwupd-enums.c +++ b/libfwupd/fwupd-enums.c @@ -57,6 +57,8 @@ fwupd_status_to_string (FwupdStatus status) return "downloading"; if (status == FWUPD_STATUS_WAITING_FOR_AUTH) return "waiting-for-auth"; + if (status == FWUPD_STATUS_SHUTDOWN) + return "shutdown"; return NULL; } @@ -99,6 +101,8 @@ fwupd_status_from_string (const gchar *status) return FWUPD_STATUS_DEVICE_BUSY; if (g_strcmp0 (status, "waiting-for-auth") == 0) return FWUPD_STATUS_WAITING_FOR_AUTH; + if (g_strcmp0 (status, "shutdown") == 0) + return FWUPD_STATUS_SHUTDOWN; return FWUPD_STATUS_LAST; } @@ -135,6 +139,8 @@ fwupd_device_flag_to_string (FwupdDeviceFlags device_flag) return "registered"; if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_REBOOT) return "needs-reboot"; + if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) + return "needs-shutdown"; if (device_flag == FWUPD_DEVICE_FLAG_REPORTED) return "reported"; if (device_flag == FWUPD_DEVICE_FLAG_NOTIFIED) @@ -189,6 +195,8 @@ fwupd_device_flag_from_string (const gchar *device_flag) return FWUPD_DEVICE_FLAG_REGISTERED; if (g_strcmp0 (device_flag, "needs-reboot") == 0) return FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + if (g_strcmp0 (device_flag, "needs-shutdown") == 0) + return FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; if (g_strcmp0 (device_flag, "reported") == 0) return FWUPD_DEVICE_FLAG_REPORTED; if (g_strcmp0 (device_flag, "notified") == 0) diff --git a/libfwupd/fwupd-enums.h b/libfwupd/fwupd-enums.h index 708c4b3d4..7a93c0b06 100644 --- a/libfwupd/fwupd-enums.h +++ b/libfwupd/fwupd-enums.h @@ -24,6 +24,7 @@ * @FWUPD_STATUS_DEVICE_ERASE: Erasing a device * @FWUPD_STATUS_WAITING_FOR_AUTH: Waiting for authentication * @FWUPD_STATUS_DEVICE_BUSY: The device is busy + * @FWUPD_STATUS_SHUTDOWN: The daemon is shutting down * * The flags to show daemon status. **/ @@ -41,6 +42,7 @@ typedef enum { FWUPD_STATUS_DEVICE_ERASE, /* Since: 1.0.0 */ FWUPD_STATUS_WAITING_FOR_AUTH, /* Since: 1.0.0 */ FWUPD_STATUS_DEVICE_BUSY, /* Since: 1.0.1 */ + FWUPD_STATUS_SHUTDOWN, /* Since: 1.2.1 */ /*< private >*/ FWUPD_STATUS_LAST } FwupdStatus; @@ -81,6 +83,7 @@ typedef enum { * @FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG: The hardware is waiting to be replugged * @FWUPD_DEVICE_FLAG_IGNORE_VALIDATION: Ignore validation safety checks when flashing this device * @FWUPD_DEVICE_FLAG_TRUSTED: Extra metadata can be exposed about this device + * @FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN: Requires system shutdown to apply firmware * * The device flags. **/ @@ -102,6 +105,7 @@ typedef enum { #define FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG (1u << 14) /* Since: 1.1.2 */ #define FWUPD_DEVICE_FLAG_IGNORE_VALIDATION (1u << 15) /* Since: 1.1.2 */ #define FWUPD_DEVICE_FLAG_TRUSTED (1u << 16) /* Since: 1.1.2 */ +#define FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN (1u << 17) /* Since: 1.2.4 */ #define FWUPD_DEVICE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 0.7.3 */ typedef guint64 FwupdDeviceFlags; diff --git a/libfwupd/fwupd-release.c b/libfwupd/fwupd-release.c index eb765b7a3..3a80ca475 100644 --- a/libfwupd/fwupd-release.c +++ b/libfwupd/fwupd-release.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 Richard Hughes + * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -33,7 +33,10 @@ typedef struct { GHashTable *metadata; gchar *description; gchar *filename; + gchar *protocol; gchar *homepage; + gchar *details_url; + gchar *source_url; gchar *appstream_id; gchar *license; gchar *name; @@ -43,7 +46,9 @@ typedef struct { gchar *version; gchar *remote_id; guint64 size; + guint32 install_duration; FwupdTrustFlags trust_flags; + gchar *update_message; } FwupdReleasePrivate; G_DEFINE_TYPE_WITH_PRIVATE (FwupdRelease, fwupd_release, G_TYPE_OBJECT) @@ -157,6 +162,78 @@ fwupd_release_set_filename (FwupdRelease *release, const gchar *filename) priv->filename = g_strdup (filename); } +/** + * fwupd_release_get_update_message: + * @release: A #FwupdRelease + * + * Gets the update message. + * + * Returns: the update message, or %NULL if unset + * + * Since: 1.2.4 + **/ +const gchar * +fwupd_release_get_update_message (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->update_message; +} + +/** + * fwupd_release_set_update_message: + * @release: A #FwupdRelease + * @update_message: the update message string + * + * Sets the update message. + * + * Since: 1.2.4 + **/ +void +fwupd_release_set_update_message (FwupdRelease *release, const gchar *update_message) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_free (priv->update_message); + priv->update_message = g_strdup (update_message); +} + +/** + * fwupd_release_get_protocol: + * @release: A #FwupdRelease + * + * Gets the update protocol. + * + * Returns: the update protocol, or %NULL if unset + * + * Since: 1.2.2 + **/ +const gchar * +fwupd_release_get_protocol (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->protocol; +} + +/** + * fwupd_release_set_protocol: + * @release: A #FwupdRelease + * @protocol: the update protocol, e.g. `org.usb.dfu` + * + * Sets the update protocol. + * + * Since: 1.2.2 + **/ +void +fwupd_release_set_protocol (FwupdRelease *release, const gchar *protocol) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_free (priv->protocol); + priv->protocol = g_strdup (protocol); +} + /** * fwupd_release_get_checksums: * @release: A #FwupdRelease @@ -355,6 +432,78 @@ fwupd_release_set_homepage (FwupdRelease *release, const gchar *homepage) priv->homepage = g_strdup (homepage); } +/** + * fwupd_release_get_details_url: + * @release: A #FwupdRelease + * + * Gets the URL for the online update notes. + * + * Returns: the update URL, or %NULL if unset + * + * Since: 1.2.4 + **/ +const gchar * +fwupd_release_get_details_url (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->details_url; +} + +/** + * fwupd_release_set_details_url: + * @release: A #FwupdRelease + * @details_url: the URL + * + * Sets the URL for the online update notes. + * + * Since: 1.2.4 + **/ +void +fwupd_release_set_details_url (FwupdRelease *release, const gchar *details_url) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_free (priv->details_url); + priv->details_url = g_strdup (details_url); +} + +/** + * fwupd_release_get_source_url: + * @release: A #FwupdRelease + * + * Gets the URL of the source code used to build this release. + * + * Returns: the update source_url, or %NULL if unset + * + * Since: 1.2.4 + **/ +const gchar * +fwupd_release_get_source_url (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->source_url; +} + +/** + * fwupd_release_set_source_url: + * @release: A #FwupdRelease + * @source_url: the URL + * + * Sets the URL of the source code used to build this release. + * + * Since: 1.2.4 + **/ +void +fwupd_release_set_source_url (FwupdRelease *release, const gchar *source_url) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_free (priv->source_url); + priv->source_url = g_strdup (source_url); +} + /** * fwupd_release_get_description: * @release: A #FwupdRelease @@ -641,6 +790,41 @@ fwupd_release_set_trust_flags (FwupdRelease *release, FwupdTrustFlags trust_flag priv->trust_flags = trust_flags; } +/** + * fwupd_release_get_install_duration: + * @release: A #FwupdRelease + * + * Gets the time estimate for firmware installation (in seconds) + * + * Returns: the estimated time to flash this release (or 0 if unset) + * + * Since: 1.2.1 + **/ +guint32 +fwupd_release_get_install_duration (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), 0); + return priv->install_duration; +} + +/** + * fwupd_release_set_install_duration: + * @release: A #FwupdRelease + * @duration: The amount of time + * + * Sets the time estimate for firmware installation (in seconds) + * + * Since: 1.2.1 + **/ +void +fwupd_release_set_install_duration (FwupdRelease *release, guint32 duration) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + priv->install_duration = duration; +} + static GVariant * _hash_kv_to_variant (GHashTable *hash) { @@ -703,6 +887,11 @@ fwupd_release_to_variant (FwupdRelease *release) FWUPD_RESULT_KEY_FILENAME, g_variant_new_string (priv->filename)); } + if (priv->protocol != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_PROTOCOL, + g_variant_new_string (priv->protocol)); + } if (priv->license != NULL) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_LICENSE, @@ -750,6 +939,16 @@ fwupd_release_to_variant (FwupdRelease *release) FWUPD_RESULT_KEY_HOMEPAGE, g_variant_new_string (priv->homepage)); } + if (priv->details_url != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_DETAILS_URL, + g_variant_new_string (priv->details_url)); + } + if (priv->source_url != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_SOURCE_URL, + g_variant_new_string (priv->source_url)); + } if (priv->version != NULL) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_VERSION, @@ -770,6 +969,11 @@ fwupd_release_to_variant (FwupdRelease *release) FWUPD_RESULT_KEY_METADATA, _hash_kv_to_variant (priv->metadata)); } + if (priv->install_duration > 0) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_INSTALL_DURATION, + g_variant_new_uint32 (priv->install_duration)); + } return g_variant_new ("a{sv}", &builder); } @@ -789,6 +993,10 @@ fwupd_release_from_key_value (FwupdRelease *release, const gchar *key, GVariant fwupd_release_set_filename (release, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_PROTOCOL) == 0) { + fwupd_release_set_protocol (release, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_LICENSE) == 0) { fwupd_release_set_license (release, g_variant_get_string (value, NULL)); return; @@ -824,6 +1032,14 @@ fwupd_release_from_key_value (FwupdRelease *release, const gchar *key, GVariant fwupd_release_set_homepage (release, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_DETAILS_URL) == 0) { + fwupd_release_set_details_url (release, g_variant_get_string (value, NULL)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_SOURCE_URL) == 0) { + fwupd_release_set_source_url (release, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_VERSION) == 0) { fwupd_release_set_version (release, g_variant_get_string (value, NULL)); return; @@ -836,6 +1052,14 @@ fwupd_release_from_key_value (FwupdRelease *release, const gchar *key, GVariant fwupd_release_set_trust_flags (release, g_variant_get_uint64 (value)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_INSTALL_DURATION) == 0) { + fwupd_release_set_install_duration (release, g_variant_get_uint32 (value)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_UPDATE_MESSAGE) == 0) { + fwupd_release_set_update_message (release, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_METADATA) == 0) { g_hash_table_unref (priv->metadata); priv->metadata = _variant_to_hash_kv (value); @@ -885,6 +1109,18 @@ fwupd_pad_kv_tfl (GString *str, const gchar *key, FwupdTrustFlags trust_flags) fwupd_pad_kv_str (str, key, tmp->str); } +static void +fwupd_pad_kv_int (GString *str, const gchar *key, guint32 value) +{ + g_autofree gchar *tmp = NULL; + + /* ignore */ + if (value == 0) + return; + tmp = g_strdup_printf("%" G_GUINT32_FORMAT, value); + fwupd_pad_kv_str (str, key, tmp); +} + /** * fwupd_release_to_string: * @release: A #FwupdRelease @@ -911,6 +1147,7 @@ fwupd_release_to_string (FwupdRelease *release) fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_VERSION, priv->version); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_FILENAME, priv->filename); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PROTOCOL, priv->protocol); for (guint i = 0; i < priv->checksums->len; i++) { const gchar *checksum = g_ptr_array_index (priv->checksums, i); g_autofree gchar *checksum_display = fwupd_checksum_format_for_display (checksum); @@ -920,9 +1157,13 @@ fwupd_release_to_string (FwupdRelease *release) fwupd_pad_kv_siz (str, FWUPD_RESULT_KEY_SIZE, priv->size); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_URI, priv->uri); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_HOMEPAGE, priv->homepage); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DETAILS_URL, priv->details_url); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_SOURCE_URL, priv->source_url); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_VENDOR, priv->vendor); fwupd_pad_kv_tfl (str, FWUPD_RESULT_KEY_TRUST_FLAGS, priv->trust_flags); - + fwupd_pad_kv_int (str, FWUPD_RESULT_KEY_INSTALL_DURATION, priv->install_duration); + if (priv->update_message != NULL) + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_UPDATE_MESSAGE, priv->update_message); /* metadata */ keys = g_hash_table_get_keys (priv->metadata); for (GList *l = keys; l != NULL; l = l->next) { @@ -957,15 +1198,19 @@ fwupd_release_finalize (GObject *object) g_free (priv->description); g_free (priv->filename); + g_free (priv->protocol); g_free (priv->appstream_id); g_free (priv->license); g_free (priv->name); g_free (priv->summary); g_free (priv->uri); g_free (priv->homepage); + g_free (priv->details_url); + g_free (priv->source_url); g_free (priv->vendor); g_free (priv->version); g_free (priv->remote_id); + g_free (priv->update_message); g_ptr_array_unref (priv->checksums); g_hash_table_unref (priv->metadata); diff --git a/libfwupd/fwupd-release.h b/libfwupd/fwupd-release.h index fad822511..4b0d9493c 100644 --- a/libfwupd/fwupd-release.h +++ b/libfwupd/fwupd-release.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 Richard Hughes + * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -54,6 +54,9 @@ const gchar *fwupd_release_get_metadata_item (FwupdRelease *release, const gchar *fwupd_release_get_filename (FwupdRelease *release); void fwupd_release_set_filename (FwupdRelease *release, const gchar *filename); +const gchar *fwupd_release_get_protocol (FwupdRelease *release); +void fwupd_release_set_protocol (FwupdRelease *release, + const gchar *protocol); const gchar *fwupd_release_get_appstream_id (FwupdRelease *release); void fwupd_release_set_appstream_id (FwupdRelease *release, const gchar *appstream_id); @@ -75,6 +78,12 @@ void fwupd_release_set_description (FwupdRelease *release, const gchar *fwupd_release_get_homepage (FwupdRelease *release); void fwupd_release_set_homepage (FwupdRelease *release, const gchar *homepage); +const gchar *fwupd_release_get_details_url (FwupdRelease *release); +void fwupd_release_set_details_url (FwupdRelease *release, + const gchar *details_url); +const gchar *fwupd_release_get_source_url (FwupdRelease *release); +void fwupd_release_set_source_url (FwupdRelease *release, + const gchar *source_url); guint64 fwupd_release_get_size (FwupdRelease *release); void fwupd_release_set_size (FwupdRelease *release, guint64 size); @@ -84,6 +93,12 @@ void fwupd_release_set_license (FwupdRelease *release, FwupdTrustFlags fwupd_release_get_trust_flags (FwupdRelease *release); void fwupd_release_set_trust_flags (FwupdRelease *release, FwupdTrustFlags trust_flags); +guint32 fwupd_release_get_install_duration (FwupdRelease *release); +void fwupd_release_set_install_duration (FwupdRelease *release, + guint32 duration); +const gchar *fwupd_release_get_update_message (FwupdRelease *release); +void fwupd_release_set_update_message (FwupdRelease *release, + const gchar *update_message); G_END_DECLS diff --git a/libfwupd/fwupd-remote.c b/libfwupd/fwupd-remote.c index 99f62e8b8..a9675ce55 100644 --- a/libfwupd/fwupd-remote.c +++ b/libfwupd/fwupd-remote.c @@ -275,6 +275,8 @@ fwupd_remote_kind_from_string (const gchar *kind) return FWUPD_REMOTE_KIND_DOWNLOAD; if (g_strcmp0 (kind, "local") == 0) return FWUPD_REMOTE_KIND_LOCAL; + if (g_strcmp0 (kind, "directory") == 0) + return FWUPD_REMOTE_KIND_DIRECTORY; return FWUPD_REMOTE_KIND_UNKNOWN; } @@ -295,6 +297,8 @@ fwupd_remote_kind_to_string (FwupdRemoteKind kind) return "download"; if (kind == FWUPD_REMOTE_KIND_LOCAL) return "local"; + if (kind == FWUPD_REMOTE_KIND_DIRECTORY) + return "directory"; return NULL; } @@ -384,7 +388,14 @@ fwupd_remote_load_from_filename (FwupdRemote *self, if (metadata_uri == NULL) return FALSE; if (g_str_has_prefix (metadata_uri, "file://")) { - priv->kind = FWUPD_REMOTE_KIND_LOCAL; + const gchar *filename_cache = metadata_uri; + if (g_str_has_prefix (filename_cache, "file://")) + filename_cache += 7; + fwupd_remote_set_filename_cache (self, filename_cache); + if (g_file_test (filename_cache, G_FILE_TEST_IS_DIR)) + priv->kind = FWUPD_REMOTE_KIND_DIRECTORY; + else + priv->kind = FWUPD_REMOTE_KIND_LOCAL; } else if (g_str_has_prefix (metadata_uri, "http://") || g_str_has_prefix (metadata_uri, "https://")) { priv->kind = FWUPD_REMOTE_KIND_DOWNLOAD; @@ -444,14 +455,6 @@ fwupd_remote_load_from_filename (FwupdRemote *self, fwupd_remote_set_filename_cache (self, filename_cache); } - /* all LOCAL remotes have to include a valid MetadataURI */ - if (priv->kind == FWUPD_REMOTE_KIND_LOCAL) { - const gchar *filename_cache = metadata_uri; - if (g_str_has_prefix (filename_cache, "file://")) - filename_cache += 7; - fwupd_remote_set_filename_cache (self, filename_cache); - } - /* load the checksum */ if (priv->filename_cache_sig != NULL && g_file_test (priv->filename_cache_sig, G_FILE_TEST_EXISTS)) { @@ -473,6 +476,25 @@ fwupd_remote_load_from_filename (FwupdRemote *self, if (firmware_base_uri != NULL) fwupd_remote_set_firmware_base_uri (self, firmware_base_uri); + /* some validation around DIRECTORY types */ + if (priv->kind == FWUPD_REMOTE_KIND_DIRECTORY) { + if (priv->keyring_kind != FWUPD_KEYRING_KIND_NONE) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Keyring kind %s is not supported with directory remote", + fwupd_keyring_kind_to_string (priv->keyring_kind)); + return FALSE; + } + if (firmware_base_uri != NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Directory remotes don't support firmware base URI"); + return FALSE; + } + } + /* dep logic */ order_before = g_key_file_get_string (kf, group, "OrderBefore", NULL); if (order_before != NULL) @@ -1099,7 +1121,7 @@ fwupd_remote_class_init (FwupdRemoteClass *klass) * Since: 0.9.3 */ pspec = g_param_spec_string ("id", NULL, NULL, - NULL, G_PARAM_READWRITE); + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_ID, pspec); /** @@ -1110,7 +1132,7 @@ fwupd_remote_class_init (FwupdRemoteClass *klass) * Since: 0.9.3 */ pspec = g_param_spec_boolean ("enabled", NULL, NULL, - FALSE, G_PARAM_READWRITE); + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_ENABLED, pspec); } diff --git a/libfwupd/fwupd-remote.h b/libfwupd/fwupd-remote.h index 2d3c885fb..144d0c2a7 100644 --- a/libfwupd/fwupd-remote.h +++ b/libfwupd/fwupd-remote.h @@ -39,6 +39,7 @@ typedef enum { FWUPD_REMOTE_KIND_UNKNOWN, FWUPD_REMOTE_KIND_DOWNLOAD, FWUPD_REMOTE_KIND_LOCAL, + FWUPD_REMOTE_KIND_DIRECTORY, /* Since: 1.2.4 */ /*< private >*/ FWUPD_REMOTE_KIND_LAST } FwupdRemoteKind; diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index 52ee9bd41..41422a0a4 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -279,3 +279,31 @@ LIBFWUPD_1.1.3 { fwupd_device_set_install_duration; local: *; } LIBFWUPD_1.1.2; + +LIBFWUPD_1.2.1 { + global: + fwupd_release_get_install_duration; + fwupd_release_set_install_duration; + local: *; +} LIBFWUPD_1.1.3; + +LIBFWUPD_1.2.2 { + global: + fwupd_release_get_protocol; + fwupd_release_set_protocol; + local: *; +} LIBFWUPD_1.2.1; + +LIBFWUPD_1.2.4 { + global: + fwupd_client_get_tainted; + fwupd_device_get_update_message; + fwupd_device_set_update_message; + fwupd_release_get_details_url; + fwupd_release_get_source_url; + fwupd_release_get_update_message; + fwupd_release_set_details_url; + fwupd_release_set_source_url; + fwupd_release_set_update_message; + local: *; +} LIBFWUPD_1.2.2; diff --git a/meson.build b/meson.build index 9e993d24f..aea7a545b 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('fwupd', 'c', - version : '1.1.4', + version : '1.2.4', license : 'LGPL-2.1+', - meson_version : '>=0.43.0', + meson_version : '>=0.47.0', default_options : ['warning_level=2', 'c_std=c99'], ) @@ -111,17 +111,12 @@ add_project_arguments(cc.get_supported_arguments(warning_flags), language : 'c') global_link_args = [] test_link_args = [ '-Wl,-z,relro', + '-Wl,-z,defs', '-Wl,-z,now', ] foreach arg: test_link_args - if meson.version().version_compare('>=0.46.0') - if cc.has_link_argument(arg) - global_link_args += arg - endif - else - if cc.has_argument(arg) - global_link_args += arg - endif + if cc.has_link_argument(arg) + global_link_args += arg endif endforeach add_global_link_arguments( @@ -160,7 +155,7 @@ gudev = dependency('gudev-1.0') if gudev.version().version_compare('>= 232') conf.set('HAVE_GUDEV_232', '1') endif -appstream_glib = dependency('appstream-glib', version : '>= 0.7.4') +libxmlb = dependency('xmlb', version : '>= 0.1.5', fallback : ['libxmlb', 'libxmlb_dep']) gusb = dependency('gusb', version : '>= 0.2.9') sqlite = dependency('sqlite3') libarchive = dependency('libarchive') @@ -198,6 +193,7 @@ if libgcab.version().version_compare('>= 1.0') endif gcab = find_program('gcab', required : true) bashcomp = dependency('bash-completion', required: false) +python3 = find_program('python3') if valgrind.found() conf.set('HAVE_VALGRIND', '1') @@ -242,8 +238,6 @@ if get_option('plugin_uefi') gnu_efi_arch = '' endif conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME) - - python3 = find_program('python3') r = run_command([python3, 'po/test-deps']) if r.returncode() != 0 error(r.stdout()) @@ -310,14 +304,8 @@ configure_file( configuration : conf ) -default_sysconfdir = get_option('sysconfdir') -if default_sysconfdir == 'etc' - message('sysconfdir of etc makes no sense, using /etc') - default_sysconfdir = '/etc' -endif - plugin_deps = [] -plugin_deps += appstream_glib +plugin_deps += libxmlb plugin_deps += gio plugin_deps += giounix plugin_deps += gmodule diff --git a/plugins/README.md b/plugins/README.md index e295facfc..42d83921f 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -11,6 +11,9 @@ If you have a firmware specification and would like to see support in this project, please file an issue and share the spec. Patches are also welcome. +We will not accept plugins that upgrade hardware using a proprietary Linux +executable, library, or DBus interface. + Plugin interaction ------------------ Some plugins may be able to influence the behavior of other plugins. diff --git a/plugins/altos/README.md b/plugins/altos/README.md index 4b3a3c272..bc21b2860 100644 --- a/plugins/altos/README.md +++ b/plugins/altos/README.md @@ -15,6 +15,25 @@ The bootloader communication is not handled in the kernel, and a tty device is created so userspace can communicate with the hardware. Commands the bootloader accept are as follows: +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +ELF file format. The firmware image is inserted into the `.text` section. + +This plugin supports the following protocol ID: + + * org.altusmetrum.altos + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_1D50&PID_60C6&REV_0001` + * `USB\VID_1D50&PID_60C6` + * `USB\VID_1D50` + ### List Information Command: `l\n` diff --git a/plugins/altos/fu-altos-device.c b/plugins/altos/fu-altos-device.c index 56bbc7a25..9087751c8 100644 --- a/plugins/altos/fu-altos-device.c +++ b/plugins/altos/fu-altos-device.c @@ -12,6 +12,7 @@ #include #include +#include "fu-io-channel.h" #include "fu-altos-device.h" #include "fu-altos-firmware.h" @@ -25,7 +26,7 @@ struct _FuAltosDevice { guint64 addr_base; guint64 addr_bound; struct termios tty_termios; - gint tty_fd; + FuIOChannel *io_channel; }; G_DEFINE_TYPE (FuAltosDevice, fu_altos_device, FU_TYPE_USB_DEVICE) @@ -149,60 +150,15 @@ fu_altos_device_tty_write (FuAltosDevice *self, gssize data_len, GError **error) { - gint rc; - gssize idx = 0; - guint timeout_ms = 500; - struct pollfd fds; - /* lets assume this is text */ if (data_len < 0) data_len = strlen (data); - - fds.fd = self->tty_fd; - fds.events = POLLOUT; - - g_debug ("write, with timeout %ums", timeout_ms); - while (idx < data_len) { - - /* wait for data to be allowed to write without blocking */ - rc = poll (&fds, 1, (gint) timeout_ms); - if (rc == 0) - break; - if (rc < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "failed to poll %i", - self->tty_fd); - return FALSE; - } - - /* we can write data */ - if (fds.revents & POLLOUT) { - gssize len; - g_debug ("writing %" G_GSSIZE_FORMAT " bytes: %s", data_len, data); - len = write (self->tty_fd, data + idx, data_len - idx); - if (len < 0) { - if (errno == EAGAIN) { - g_debug ("got EAGAIN, trying harder"); - continue; - } - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to write %" G_GSSIZE_FORMAT - " bytes to %i: %s" , - data_len, - self->tty_fd, - strerror (errno)); - return FALSE; - } - g_debug ("wrote %" G_GSSIZE_FORMAT " bytes", len); - idx += len; - } - } - - return TRUE; + return fu_io_channel_write_raw (self->io_channel, + (const guint8 *) data, + (gsize) data_len, + 500, /* ms */ + FU_IO_CHANNEL_FLAG_NONE, + error); } static GString * @@ -211,90 +167,12 @@ fu_altos_device_tty_read (FuAltosDevice *self, gssize max_size, GError **error) { - gint rc; - struct pollfd fds; - g_autoptr(GString) str = g_string_new (NULL); - - fds.fd = self->tty_fd; - fds.events = POLLIN; - - g_debug ("read, with timeout %ums", timeout_ms); - for (;;) { - /* wait for data to appear */ - rc = poll (&fds, 1, (gint) timeout_ms); - if (rc == 0) - break; - if (rc < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "failed to poll %i", - self->tty_fd); - return NULL; - } - - /* we have data to read */ - if (fds.revents & POLLIN) { - guint8 buf[1024]; - gssize len = read (self->tty_fd, buf, sizeof (buf)); - if (len < 0) { - if (errno == EAGAIN) { - g_debug ("got EAGAIN, trying harder"); - continue; - } - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "failed to read %i: %s", - self->tty_fd, - strerror (errno)); - return NULL; - } - if (len > 0) { - g_debug ("read %" G_GSSIZE_FORMAT " bytes from device", len); - g_string_append_len (str, (gchar *) buf, len); - } - - /* check maximum size */ - if (max_size > 0 && str->len >= (guint) max_size) - break; - continue; - } - if (fds.revents & POLLERR) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "error condition"); - return NULL; - } - if (fds.revents & POLLHUP) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "connection hung up"); - return NULL; - } - if (fds.revents & POLLNVAL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "invalid request"); - return NULL; - } - } - - /* no data */ - if (str->len == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "no data received from device in %ums", - timeout_ms); + g_autoptr(GBytes) buf = NULL; + buf = fu_io_channel_read_bytes (self->io_channel, max_size, + timeout_ms, FU_IO_CHANNEL_FLAG_NONE, error); + if (buf == NULL) return NULL; - } - - /* return blob */ - return g_steal_pointer (&str); + return g_string_new_len (g_bytes_get_data (buf, NULL), g_bytes_get_size (buf)); } static gboolean @@ -304,18 +182,12 @@ fu_altos_device_tty_open (FuAltosDevice *self, GError **error) g_autoptr(GString) str = NULL; /* open device */ - self->tty_fd = open (self->tty, O_RDWR | O_NONBLOCK); - if (self->tty_fd < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "failed to open %s", - self->tty); + self->io_channel = fu_io_channel_new_file (self->tty, error); + if (self->io_channel == NULL) return FALSE; - } /* get the old termios settings so we can restore later */ - if (tcgetattr (self->tty_fd, &termios) < 0) { + if (tcgetattr (fu_io_channel_unix_get_fd (self->io_channel), &termios) < 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -340,7 +212,8 @@ fu_altos_device_tty_open (FuAltosDevice *self, GError **error) termios.c_cc[VTIME] = 0; /* set all new data */ - if (tcsetattr (self->tty_fd, TCSAFLUSH, &termios) < 0) { + if (tcsetattr (fu_io_channel_unix_get_fd (self->io_channel), + TCSAFLUSH, &termios) < 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -359,10 +232,11 @@ fu_altos_device_tty_open (FuAltosDevice *self, GError **error) static gboolean fu_altos_device_tty_close (FuAltosDevice *self, GError **error) { - - tcsetattr (self->tty_fd, TCSAFLUSH, &self->tty_termios); - close (self->tty_fd); - + tcsetattr (fu_io_channel_unix_get_fd (self->io_channel), + TCSAFLUSH, &self->tty_termios); + if (!fu_io_channel_shutdown (self->io_channel, error)) + return FALSE; + g_clear_object (&self->io_channel); return TRUE; } diff --git a/plugins/altos/fu-plugin-altos.c b/plugins/altos/fu-plugin-altos.c index a7b7520a3..9ddcd460e 100644 --- a/plugins/altos/fu-plugin-altos.c +++ b/plugins/altos/fu-plugin-altos.c @@ -13,7 +13,9 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.altusmetrum.altos"); } gboolean diff --git a/plugins/altos/meson.build b/plugins/altos/meson.build index eb0300b96..55cb3d841 100644 --- a/plugins/altos/meson.build +++ b/plugins/altos/meson.build @@ -5,6 +5,7 @@ install_data(['altos.quirk'], ) shared_module('fu_plugin_altos', + fu_hash, sources : [ 'fu-altos-device.c', 'fu-altos-firmware.c', @@ -17,6 +18,9 @@ shared_module('fu_plugin_altos', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ libelf, diff --git a/plugins/amt/README.md b/plugins/amt/README.md index 4a053b229..5494815a4 100644 --- a/plugins/amt/README.md +++ b/plugins/amt/README.md @@ -15,3 +15,8 @@ which can be found here: https://github.com/mjg59/mei-amt-check 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 GUID provided by the AMT host interface. diff --git a/plugins/amt/fu-plugin-amt.c b/plugins/amt/fu-plugin-amt.c index e68223176..44bdf85be 100644 --- a/plugins/amt/fu-plugin-amt.c +++ b/plugins/amt/fu-plugin-amt.c @@ -86,27 +86,26 @@ mei_context_new (mei_context *ctx, return TRUE; } -static gssize +static gboolean mei_recv_msg (mei_context *ctx, guchar *buffer, - gssize len, unsigned long timeout, GError **error) + gssize len, guint32 *readsz, unsigned long timeout, GError **error) { gssize rc; - - g_debug ("call read length = %zd", len); rc = read (ctx->fd, buffer, len); if (rc < 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_READ, "read failed with status %zd %s", - rc, strerror(errno)); - } else { - g_debug ("read succeeded with result %zd", rc); + rc, strerror(errno)); + return FALSE; } - return rc; + if (readsz != NULL) + *readsz = rc; + return TRUE; } -static gssize +static gboolean mei_send_msg (mei_context *ctx, const guchar *buffer, gssize len, unsigned long timeout, GError **error) { @@ -118,7 +117,6 @@ mei_send_msg (mei_context *ctx, const guchar *buffer, tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000000; - g_debug ("call write length = %zd", len); written = write (ctx->fd, buffer, len); if (written < 0) { g_set_error (error, @@ -126,16 +124,22 @@ mei_send_msg (mei_context *ctx, const guchar *buffer, FWUPD_ERROR_WRITE, "write failed with status %zd %s", written, strerror(errno)); - return -errno; + return FALSE; + } + if (written != len) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "only wrote %" G_GSSIZE_FORMAT " of %" G_GSSIZE_FORMAT, + written, len); + return FALSE; } FD_ZERO(&set); FD_SET(ctx->fd, &set); rc = select (ctx->fd + 1 , &set, NULL, NULL, &tv); - if (rc > 0 && FD_ISSET(ctx->fd, &set)) { - g_debug ("write success"); - return written; - } + if (rc > 0 && FD_ISSET(ctx->fd, &set)) + return TRUE; /* timed out */ if (rc == 0) { @@ -143,7 +147,7 @@ mei_send_msg (mei_context *ctx, const guchar *buffer, FWUPD_ERROR, FWUPD_ERROR_WRITE, "write failed on timeout with status"); - return 0; + return FALSE; } /* rc < 0 */ @@ -151,7 +155,7 @@ mei_send_msg (mei_context *ctx, const guchar *buffer, FWUPD_ERROR, FWUPD_ERROR_WRITE, "write failed on select with status %zd", rc); - return rc; + return FALSE; } /*************************************************************************** @@ -254,50 +258,91 @@ struct amt_host_if { mei_context mei_cl; }; -static guint32 -amt_verify_code_versions (const struct amt_host_if_resp_header *resp) +static gboolean +amt_verify_code_versions (const struct amt_host_if_resp_header *resp, GError **error) { struct amt_code_versions *code_ver = (struct amt_code_versions *)resp->data; gsize code_ver_len = resp->header.length - sizeof(guint32); guint32 ver_type_cnt = code_ver_len - sizeof(code_ver->bios) - sizeof(code_ver->count); - if (code_ver->count != ver_type_cnt / sizeof(struct amt_version_type)) - return AMT_STATUS_INTERNAL_ERROR; + if (code_ver->count != ver_type_cnt / sizeof(struct amt_version_type)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid offset"); + return FALSE; + } for (guint32 i = 0; i < code_ver->count; i++) { guint32 len = code_ver->versions[i].description.length; - if (len > AMT_UNICODE_STRING_LEN) - return AMT_STATUS_INTERNAL_ERROR; + if (len > AMT_UNICODE_STRING_LEN) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "string too large"); + return FALSE; + } len = code_ver->versions[i].version.length; if (code_ver->versions[i].version.string[len] != '\0' || - len != strlen(code_ver->versions[i].version.string)) - return AMT_STATUS_INTERNAL_ERROR; + len != strlen(code_ver->versions[i].version.string)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "string was invalid size"); + return FALSE; + } } - return AMT_STATUS_SUCCESS; + return TRUE; } -static guint32 -amt_verify_response_header (guint32 command, - const struct amt_host_if_msg_header *resp_hdr, - guint32 response_size) +static gboolean +amt_status_set_error (guint32 status, GError **error) { - if (response_size < sizeof(struct amt_host_if_resp_header)) { - return AMT_STATUS_INTERNAL_ERROR; - } else if (response_size != (resp_hdr->length + - sizeof(struct amt_host_if_msg_header))) { - return AMT_STATUS_INTERNAL_ERROR; - } else if (resp_hdr->command != command) { - return AMT_STATUS_INTERNAL_ERROR; - } else if (resp_hdr->_reserved != 0) { - return AMT_STATUS_INTERNAL_ERROR; - } else if (resp_hdr->version.major != AMT_MAJOR_VERSION || - resp_hdr->version.minor < AMT_MINOR_VERSION) { - return AMT_STATUS_INTERNAL_ERROR; + if (status == AMT_STATUS_SUCCESS) + return TRUE; + if (status == AMT_STATUS_INTERNAL_ERROR) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error"); + return FALSE; } - return AMT_STATUS_SUCCESS; + if (status == AMT_STATUS_NOT_READY) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "not ready"); + return FALSE; + } + if (status == AMT_STATUS_INVALID_AMT_MODE) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid AMT mode"); + return FALSE; + } + if (status == AMT_STATUS_INVALID_MESSAGE_LENGTH) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid message length"); + return FALSE; + } + if (status == 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 guint32 +static gboolean amt_host_if_call (mei_context *mei_cl, const guchar *command, gssize command_sz, @@ -309,53 +354,86 @@ amt_host_if_call (mei_context *mei_cl, { guint32 in_buf_sz; guint32 out_buf_sz; - gssize written; - guint32 status; struct amt_host_if_resp_header *msg_hdr; in_buf_sz = mei_cl->buf_size; *read_buf = (guint8 *) g_malloc0 (in_buf_sz); msg_hdr = (struct amt_host_if_resp_header *) *read_buf; - written = mei_send_msg (mei_cl, command, command_sz, send_timeout, error); - if (written != command_sz) - return AMT_STATUS_INTERNAL_ERROR; - - out_buf_sz = mei_recv_msg (mei_cl, *read_buf, in_buf_sz, 2000, error); - if (out_buf_sz <= 0) - return AMT_STATUS_HOST_IF_EMPTY_RESPONSE; - - status = msg_hdr->status; - if (status != AMT_STATUS_SUCCESS) - return status; - - status = amt_verify_response_header(rcmd, &msg_hdr->header, out_buf_sz); - if (status != AMT_STATUS_SUCCESS) - return status; - - if (expected_sz && expected_sz != out_buf_sz) - return AMT_STATUS_INTERNAL_ERROR; - - return AMT_STATUS_SUCCESS; + if (!mei_send_msg (mei_cl, command, command_sz, send_timeout, error)) + return FALSE; + if (!mei_recv_msg (mei_cl, *read_buf, in_buf_sz, &out_buf_sz, 2000, error)) + return FALSE; + if (out_buf_sz <= 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "empty response"); + return FALSE; + } + if (expected_sz && expected_sz != out_buf_sz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "expected %u but got %" G_GUINT32_FORMAT, + expected_sz, out_buf_sz); + return FALSE; + } + if (!amt_status_set_error (msg_hdr->status, error)) + return FALSE; + if (out_buf_sz < sizeof(struct amt_host_if_resp_header)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "invalid response: too small"); + return FALSE; + } + if (out_buf_sz != (msg_hdr->header.length + + sizeof(struct amt_host_if_msg_header))) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "invalid response: headerlen"); + return FALSE; + } + if (msg_hdr->header.command != rcmd) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "invalid response: rcmd"); + return FALSE; + } + if (msg_hdr->header._reserved != 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "invalid response: reserved"); + return FALSE; + } + if (msg_hdr->header.version.major != AMT_MAJOR_VERSION || + msg_hdr->header.version.minor < AMT_MINOR_VERSION) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "invalid response: version"); + return FALSE; + } + return TRUE; } -static guint32 +static gboolean amt_get_provisioning_state (mei_context *mei_cl, guint8 *state, GError **error) { g_autofree struct amt_host_if_resp_header *response = NULL; - guint32 status; - - status = amt_host_if_call (mei_cl, - (const guchar *)&PROVISIONING_STATE_REQUEST, - sizeof(PROVISIONING_STATE_REQUEST), - (guint8 **)&response, - AMT_HOST_IF_PROVISIONING_STATE_RESPONSE, 0, - 5000, error); - if (status != AMT_STATUS_SUCCESS) { + if (!amt_host_if_call (mei_cl, + (const guchar *)&PROVISIONING_STATE_REQUEST, + sizeof(PROVISIONING_STATE_REQUEST), + (guint8 **)&response, + AMT_HOST_IF_PROVISIONING_STATE_RESPONSE, 0, + 5000, error)) { g_prefix_error (error, "unable to get provisioning state: "); return FALSE; } - *state = response->data[0]; return TRUE; } @@ -369,12 +447,13 @@ static FuDevice * fu_plugin_amt_create_device (GError **error) { gchar guid_buf[37]; - guint32 status; guint8 state; struct amt_code_versions ver; uuid_t uu; g_autofree struct amt_host_if_resp_header *response = NULL; g_autoptr(FuDevice) dev = NULL; + g_autoptr(GString) version_bl = g_string_new (NULL); + g_autoptr(GString) version_fw = g_string_new (NULL); g_autoptr(mei_context) ctx = g_new0 (mei_context, 1); const uuid_le MEI_IAMTHIF = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, \ @@ -385,33 +464,18 @@ fu_plugin_amt_create_device (GError **error) return NULL; /* check version */ - status = amt_host_if_call (ctx, - (const guchar *) &CODE_VERSION_REQ, - sizeof(CODE_VERSION_REQ), - (guint8 **) &response, - AMT_HOST_IF_CODE_VERSIONS_RESPONSE, 0, - 5000, - error); - if (status != AMT_STATUS_SUCCESS) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Failed to check version"); + if (!amt_host_if_call (ctx, + (const guchar *) &CODE_VERSION_REQ, + sizeof(CODE_VERSION_REQ), + (guint8 **) &response, + AMT_HOST_IF_CODE_VERSIONS_RESPONSE, 0, + 5000, + error)) { + g_prefix_error (error, "Failed to check version: "); return NULL; } - status = amt_verify_code_versions (response); - if (status == AMT_STATUS_HOST_IF_EMPTY_RESPONSE) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Intel AMT is disabled"); - return NULL; - } - if (status != AMT_STATUS_SUCCESS) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Failed to verify code versions"); + if (!amt_verify_code_versions (response, error)) { + g_prefix_error (error, "failed to verify code versions: "); return NULL; } memcpy (&ver, response->data, sizeof(struct amt_code_versions)); @@ -449,18 +513,38 @@ fu_plugin_amt_create_device (GError **error) /* get version numbers */ for (guint i = 0; i < ver.count; i++) { if (g_strcmp0 (ver.versions[i].description.string, "AMT") == 0) { - fu_device_set_version (dev, ver.versions[i].version.string); + g_string_append (version_fw, ver.versions[i].version.string); continue; } - if (g_strcmp0 (ver.versions[i].description.string, - "Recovery Version") == 0) { - fu_device_set_version_bootloader (dev, ver.versions[i].version.string); + if (g_strcmp0 (ver.versions[i].description.string, "Recovery Version") == 0) { + g_string_append (version_bl, ver.versions[i].version.string); + continue; + } + if (g_strcmp0 (ver.versions[i].description.string, "Build Number") == 0) { + g_string_append_printf (version_fw, ".%s", + ver.versions[i].version.string); + continue; + } + if (g_strcmp0 (ver.versions[i].description.string, "Recovery Build Num") == 0) { + g_string_append_printf (version_bl, ".%s", + ver.versions[i].version.string); continue; } } + if (version_fw->len > 0) + fu_device_set_version (dev, version_fw->str); + if (version_bl->len > 0) + fu_device_set_version_bootloader (dev, version_bl->str); + return g_steal_pointer (&dev); } +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { diff --git a/plugins/amt/meson.build b/plugins/amt/meson.build index b3711974d..0904a2ace 100644 --- a/plugins/amt/meson.build +++ b/plugins/amt/meson.build @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginAmt"'] shared_module('fu_plugin_amt', + fu_hash, sources : [ 'fu-plugin-amt.c', ], @@ -11,6 +12,9 @@ shared_module('fu_plugin_amt', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/ata/README.md b/plugins/ata/README.md new file mode 100644 index 000000000..decbfe28c --- /dev/null +++ b/plugins/ata/README.md @@ -0,0 +1,44 @@ +ATA +=== + +Introduction +------------ + +This plugin allows updating ATA/ATAPI storage hardware. Devices are enumerated +from the block devices and if ID_ATA_DOWNLOAD_MICROCODE is supported they can +be updated with appropriate firmware file. + +Updating ATA devices is more dangerous than other hardware such as DFU or NVMe +and should be tested carefully with the help of the drive vendor. + +The device GUID is read from the trimmed model string. + +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: + + * org.t13.ata + +GUID Generation +--------------- + +These device use the Microsoft DeviceInstanceId values, e.g. + + * `IDE\VENDOR[40]REVISION[8]` + * `IDE\0VENDOR[40]` + +See https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-ide-devices +for more details. + +Quirk use +--------- +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|------------------------|-------------------------------------------|-----------------------| +| `AtaTransferBlocks` | Blocks to transfer, or `0xffff` for max | 1.2.4 | +| `AtaTransferMode` | The transfer mode, `0x3`, `0x7` or `0xe` | 1.2.4 | diff --git a/plugins/ata/ata.quirk b/plugins/ata/ata.quirk new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/plugins/ata/ata.quirk @@ -0,0 +1 @@ + diff --git a/plugins/ata/fu-ata-device.c b/plugins/ata/fu-ata-device.c new file mode 100644 index 000000000..4210e00e2 --- /dev/null +++ b/plugins/ata/fu-ata-device.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "fu-ata-device.h" +#include "fu-chunk.h" + +#define FU_ATA_IDENTIFY_SIZE 512 /* bytes */ +#define FU_ATA_BLOCK_SIZE 512 /* bytes */ + +struct ata_tf { + guint8 dev; + guint8 command; + guint8 error; + guint8 status; + guint8 feat; + guint8 nsect; + guint8 lbal; + guint8 lbam; + guint8 lbah; +}; + +#define ATA_USING_LBA (1 << 6) +#define ATA_STAT_DRQ (1 << 3) +#define ATA_STAT_ERR (1 << 0) + +#define ATA_OP_IDENTIFY 0xec +#define ATA_OP_DOWNLOAD_MICROCODE 0x92 + +#define ATA_SUBCMD_MICROCODE_OBSOLETE 0x01 +#define ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS_ACTIVATE 0x03 +#define ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNK 0x07 +#define ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS 0x0e +#define ATA_SUBCMD_MICROCODE_ACTIVATE 0x0f + +#define SG_CHECK_CONDITION 0x02 +#define SG_DRIVER_SENSE 0x08 + +#define SG_ATA_12 0xa1 +#define SG_ATA_12_LEN 12 + +#define SG_ATA_PROTO_NON_DATA (3 << 1) +#define SG_ATA_PROTO_PIO_IN (4 << 1) +#define SG_ATA_PROTO_PIO_OUT (5 << 1) + +enum { + SG_CDB2_TLEN_NODATA = 0 << 0, + SG_CDB2_TLEN_FEAT = 1 << 0, + SG_CDB2_TLEN_NSECT = 2 << 0, + + SG_CDB2_TLEN_BYTES = 0 << 2, + SG_CDB2_TLEN_SECTORS = 1 << 2, + + SG_CDB2_TDIR_TO_DEV = 0 << 3, + SG_CDB2_TDIR_FROM_DEV = 1 << 3, + + SG_CDB2_CHECK_COND = 1 << 5, +}; + +struct _FuAtaDevice { + FuUdevDevice parent_instance; + guint pci_depth; + gint fd; + guint16 transfer_blocks; + guint8 transfer_mode; +}; + +G_DEFINE_TYPE (FuAtaDevice, fu_ata_device, FU_TYPE_UDEV_DEVICE) + +#ifndef HAVE_GUDEV_232 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) +#pragma clang diagnostic pop +#endif + +guint8 +fu_ata_device_get_transfer_mode (FuAtaDevice *self) +{ + return self->transfer_mode; +} + +guint16 +fu_ata_device_get_transfer_blocks (FuAtaDevice *self) +{ + return self->transfer_blocks; +} + +static gchar * +fu_ata_device_get_string (const guint16 *buf, guint start, guint end) +{ + g_autoptr(GString) str = g_string_new (NULL); + for (guint i = start; i <= end; i++) { + g_string_append_c (str, (gchar) (buf[i] >> 8)); + g_string_append_c (str, (gchar) (buf[i] & 0xff)); + } + + /* remove whitespace before returning */ + if (str->len > 0) { + g_strchomp (str->str); + if (str->str[0] == '\0') + return NULL; + } + return g_string_free (g_steal_pointer (&str), FALSE); +} + +static void +fu_ata_device_to_string (FuDevice *device, GString *str) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + g_string_append (str, " FuAtaDevice:\n"); + g_string_append_printf (str, " fd:\t\t\t%i\n", self->fd); + g_string_append_printf (str, " transfer-mode:\t0x%x\n", (guint) self->transfer_mode); + g_string_append_printf (str, " transfer-size:\t0x%x\n", (guint) self->transfer_blocks); + g_string_append_printf (str, " pci-depth:\t\t%u\n", self->pci_depth); +} + +/* https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-ide-devices */ +static gchar * +fu_ata_device_pad_string_for_id (const gchar *name) +{ + GString *str = g_string_new (name); + fu_common_string_replace (str, " ", "_"); + for (guint i = str->len; i < 40; i++) + g_string_append_c (str, '_'); + return g_string_free (str, FALSE); +} + +static gboolean +fu_ata_device_parse_id (FuAtaDevice *self, const guint8 *buf, gsize sz, GError **error) +{ + FuDevice *device = FU_DEVICE (self); + guint16 xfer_min = 1; + guint16 xfer_max = 0xffff; + guint16 id[FU_ATA_IDENTIFY_SIZE/2]; + g_autofree gchar *name_pad = NULL; + g_autofree gchar *sku = NULL; + + /* check size */ + if (sz != FU_ATA_IDENTIFY_SIZE) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "ID incorrect size, got 0x%02x", + (guint) sz); + return FALSE; + } + + /* read LE buffer */ + for (guint i = 0; i < sz / 2; i++) + id[i] = fu_common_read_uint16 (buf + (i * 2), G_LITTLE_ENDIAN); + + /* verify drive correctly supports DOWNLOAD_MICROCODE */ + if (!(id[83] & 1 && id[86] & 1)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "DOWNLOAD_MICROCODE not supported by device"); + return FALSE; + } + + /* firmware will be applied when the device restarts */ + if (self->transfer_mode == ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS) + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + + /* the newer, segmented transfer mode */ + if (self->transfer_mode == ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS_ACTIVATE || + self->transfer_mode == ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS) { + xfer_min = id[234]; + if (xfer_min == 0x0 || xfer_min == 0xffff) + xfer_min = 1; + xfer_max = id[235]; + if (xfer_max == 0x0 || xfer_max == 0xffff) + xfer_max = xfer_min; + } + + /* fall back to a sane block size */ + if (self->transfer_blocks == 0x0) + self->transfer_blocks = xfer_min; + else if (self->transfer_blocks == 0xffff) + self->transfer_blocks = xfer_max; + + /* get values in case the kernel didn't */ + if (fu_device_get_serial (device) == NULL) { + g_autofree gchar *tmp = NULL; + tmp = fu_ata_device_get_string (id, 10, 19); + fu_device_set_serial (device, tmp); + } + if (fu_device_get_name (device) == NULL) { + g_autofree gchar *tmp = NULL; + tmp = fu_ata_device_get_string (id, 27, 46); + fu_device_set_name (device, tmp); + } + if (fu_device_get_version (device) == NULL) { + g_autofree gchar *tmp = NULL; + tmp = fu_ata_device_get_string (id, 23, 26); + fu_device_set_version (device, tmp); + } + + /* 8 byte additional product identifier == SKU? */ + sku = fu_ata_device_get_string (id, 170, 173); + if (sku != NULL) + g_debug ("SKU=%s", sku); + + /* add extra GUIDs */ + name_pad = fu_ata_device_pad_string_for_id (fu_device_get_name (device)); + if (name_pad != NULL && + fu_device_get_version (device) != NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("IDE\\%s%s", name_pad, + fu_device_get_version (device)); + fu_device_add_guid (device, tmp); + } + if (name_pad != NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("IDE\\0%s", name_pad); + fu_device_add_guid (device, tmp); + } + + return TRUE; +} + +static gboolean +fu_ata_device_open (FuDevice *device, GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + + /* open device */ + self->fd = g_open (g_udev_device_get_device_file (udev_device), O_RDONLY); + if (self->fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open %s: %s", + g_udev_device_get_device_file (udev_device), + strerror (errno)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ata_device_probe (FuUdevDevice *device, GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id (device, "scsi", error)) + return FALSE; + + /* look at the PCI depth to work out if in an external enclosure */ + self->pci_depth = fu_udev_device_get_slot_depth (device, "pci"); + if (self->pci_depth <= 2) + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); + + return TRUE; +} + +static guint64 +fu_ata_device_tf_to_pack_id (struct ata_tf *tf) +{ + guint32 lba24 = (tf->lbah << 16) | (tf->lbam << 8) | (tf->lbal); + guint32 lbah = tf->dev & 0x0f; + return (((guint64) lbah) << 24) | (guint64) lba24; +} + +static gboolean +fu_ata_device_command (FuAtaDevice *self, struct ata_tf *tf, + gint dxfer_direction, guint timeout_ms, + guint8 *dxferp, gsize dxfer_len, GError **error) +{ + guint8 cdb[SG_ATA_12_LEN] = { 0x0 }; + guint8 sb[32] = { 0x0 }; + sg_io_hdr_t io_hdr = { 0x0 }; + + /* map _TO_DEV to PIO mode */ + if (dxfer_direction == SG_DXFER_TO_DEV) + cdb[1] = SG_ATA_PROTO_PIO_OUT; + else if (dxfer_direction == SG_DXFER_FROM_DEV) + cdb[1] = SG_ATA_PROTO_PIO_IN; + else + cdb[1] = SG_ATA_PROTO_NON_DATA; + + /* libata workaround: don't demand sense data for IDENTIFY */ + if (dxfer_len > 0) { + cdb[2] |= SG_CDB2_TLEN_NSECT | SG_CDB2_TLEN_SECTORS; + cdb[2] |= dxfer_direction == SG_DXFER_TO_DEV ? SG_CDB2_TDIR_TO_DEV : SG_CDB2_TDIR_FROM_DEV; + } else { + cdb[2] = SG_CDB2_CHECK_COND; + } + + /* populate non-LBA48 CDB */ + cdb[0] = SG_ATA_12; + cdb[3] = tf->feat; + cdb[4] = tf->nsect; + cdb[5] = tf->lbal; + cdb[6] = tf->lbam; + cdb[7] = tf->lbah; + cdb[8] = tf->dev; + cdb[9] = tf->command; + fu_common_dump_raw (G_LOG_DOMAIN, "CBD", cdb, sizeof(cdb)); + if (dxfer_direction == SG_DXFER_TO_DEV && dxferp != NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "outgoing_data", + dxferp, dxfer_len); + } + + /* hit hardware */ + io_hdr.interface_id = 'S'; + io_hdr.mx_sb_len = sizeof(sb); + io_hdr.dxfer_direction = dxfer_direction; + io_hdr.dxfer_len = dxfer_len; + io_hdr.dxferp = dxferp; + io_hdr.cmdp = cdb; + io_hdr.cmd_len = SG_ATA_12_LEN; + io_hdr.sbp = sb; + io_hdr.pack_id = fu_ata_device_tf_to_pack_id (tf); + io_hdr.timeout = timeout_ms; + if (ioctl (self->fd, SG_IO, &io_hdr) == -1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "SG_IO not supported: %s", + strerror (errno)); + return FALSE; + } + g_debug ("ATA_%u status=0x%x, host_status=0x%x, driver_status=0x%x", + io_hdr.cmd_len, io_hdr.status, io_hdr.host_status, io_hdr.driver_status); + fu_common_dump_raw (G_LOG_DOMAIN, "SB", sb, sizeof(sb)); + + /* error check */ + if (io_hdr.status && io_hdr.status != SG_CHECK_CONDITION) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "bad status: 0x%x", io_hdr.status); + return FALSE; + } + if (io_hdr.host_status) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "bad host status: 0x%x", io_hdr.host_status); + return FALSE; + } + if (io_hdr.driver_status && (io_hdr.driver_status != SG_DRIVER_SENSE)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "bad driver status: 0x%x", io_hdr.driver_status); + return FALSE; + } + + /* repopulate ata_tf */ + tf->error = sb[8 + 3]; + tf->nsect = sb[8 + 5]; + tf->lbal = sb[8 + 7]; + tf->lbam = sb[8 + 9]; + tf->lbah = sb[8 + 11]; + tf->dev = sb[8 + 12]; + tf->status = sb[8 + 13]; + g_debug ("ATA_%u stat=%02x err=%02x nsect=%02x lbal=%02x lbam=%02x lbah=%02x dev=%02x", + io_hdr.cmd_len, tf->status, tf->error, tf->nsect, tf->lbal, tf->lbam, tf->lbah, tf->dev); + + /* io error */ + if (tf->status & (ATA_STAT_ERR | ATA_STAT_DRQ)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "I/O error, ata_op=0x%02x ata_status=0x%02x ata_error=0x%02x", + tf->command, tf->status, tf->error); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ata_device_setup (FuDevice *device, GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + struct ata_tf tf = { 0x0 }; + guint8 id[FU_ATA_IDENTIFY_SIZE]; + + /* get ID block */ + tf.dev = ATA_USING_LBA; + 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"); + return FALSE; + } + if (!fu_ata_device_parse_id (self, id, sizeof(id), error)) + return FALSE; + + /* add the name fallback */ + fu_device_add_guid (device, fu_device_get_name (device)); + + /* success */ + return TRUE; +} + +static gboolean +fu_ata_device_close (FuDevice *device, GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + if (!g_close (self->fd, error)) + return FALSE; + self->fd = 0; + return TRUE; +} + +static gboolean +fu_ata_device_fw_download (FuAtaDevice *self, + guint32 idx, + guint32 addr, + const guint8 *data, + guint32 data_sz, + GError **error) +{ + struct ata_tf tf = { 0x0 }; + guint32 block_count = data_sz / FU_ATA_BLOCK_SIZE; + guint32 buffer_offset = addr / FU_ATA_BLOCK_SIZE; + + /* write block */ + tf.dev = 0xa0 | ATA_USING_LBA; + tf.command = ATA_OP_DOWNLOAD_MICROCODE; + tf.feat = self->transfer_mode; + tf.nsect = block_count & 0xff; + tf.lbal = block_count >> 8; + tf.lbam = buffer_offset & 0xff; + tf.lbah = buffer_offset >> 8; + if (!fu_ata_device_command (self, &tf, SG_DXFER_TO_DEV, + 120 * 1000, /* a long time! */ + (guint8 *) data, data_sz, error)) { + g_prefix_error (error, "failed to write firmware @0x%0x", + (guint) addr); + return FALSE; + } + + /* check drive status */ + if (tf.nsect == 0x0) + return TRUE; + + /* drive wants more data, or thinks it is all done */ + if (tf.nsect == 0x1 || tf.nsect == 0x2) + return TRUE; + + /* the offset was set up incorrectly */ + if (tf.nsect == 0x4) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "alignment error"); + return FALSE; + } + + /* other error */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unknown return code 0x%02x", + tf.nsect); + return FALSE; +} + +static gboolean +fu_ata_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + guint32 chunksz = (guint32) self->transfer_blocks * FU_ATA_BLOCK_SIZE; + guint max_size = 0xffff * FU_ATA_BLOCK_SIZE; + g_autoptr(GPtrArray) chunks = NULL; + + /* only one block allowed */ + if (self->transfer_mode == ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNK) + max_size = 0xffff; + + /* check is valid */ + if (g_bytes_get_size (fw) > max_size) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware is too large, maximum size is %u", + max_size); + return FALSE; + } + if (g_bytes_get_size (fw) % FU_ATA_BLOCK_SIZE != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware is not multiple of block size %i", + FU_ATA_BLOCK_SIZE); + return FALSE; + } + + /* write each block */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + chunks = fu_chunk_array_new_from_bytes (fw, 0x00, 0x00, chunksz); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (!fu_ata_device_fw_download (self, + chk->idx, + chk->address, + chk->data, + chk->data_sz, + error)) { + g_prefix_error (error, "failed to write chunk %u: ", i); + return FALSE; + } + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len + 1); + } + + /* success! */ + fu_device_set_progress (device, 100); + return TRUE; +} + +static gboolean +fu_ata_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + if (g_strcmp0 (key, "AtaTransferMode") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp != ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS_ACTIVATE && + tmp != ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS && + tmp != ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNK) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "AtaTransferMode only supports " + "values 0x3, 0x7 or 0xe"); + return FALSE; + } + self->transfer_mode = (guint8) tmp; + return TRUE; + } + if (g_strcmp0 (key, "AtaTransferBlocks") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp > 0xffff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "AtaTransferBlocks only supports " + "values <= 0xffff"); + return FALSE; + } + self->transfer_blocks = (guint16) tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_ata_device_init (FuAtaDevice *self) +{ + /* we chose this default as _DOWNLOAD_CHUNKS_ACTIVATE applies the + * firmware straight away and the kernel might not like the unexpected + * ATA restart and panic */ + self->transfer_mode = ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNKS; + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_summary (FU_DEVICE (self), "ATA Drive"); + fu_device_add_icon (FU_DEVICE (self), "drive-harddisk"); +} + +static void +fu_ata_device_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_ata_device_parent_class)->finalize (object); +} + +static void +fu_ata_device_class_init (FuAtaDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); + object_class->finalize = fu_ata_device_finalize; + klass_device->to_string = fu_ata_device_to_string; + klass_device->set_quirk_kv = fu_ata_device_set_quirk_kv; + klass_device->open = fu_ata_device_open; + klass_device->setup = fu_ata_device_setup; + klass_device->close = fu_ata_device_close; + klass_device->write_firmware = fu_ata_device_write_firmware; + klass_udev_device->probe = fu_ata_device_probe; +} + +FuAtaDevice * +fu_ata_device_new (FuUdevDevice *device) +{ + FuAtaDevice *self = g_object_new (FU_TYPE_ATA_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} + +FuAtaDevice * +fu_ata_device_new_from_blob (const guint8 *buf, gsize sz, GError **error) +{ + g_autoptr(FuAtaDevice) self = g_object_new (FU_TYPE_ATA_DEVICE, NULL); + if (!fu_ata_device_parse_id (self, buf, sz, error)) + return NULL; + return g_steal_pointer (&self); +} diff --git a/plugins/ata/fu-ata-device.h b/plugins/ata/fu-ata-device.h new file mode 100644 index 000000000..ce0619aa4 --- /dev/null +++ b/plugins/ata/fu-ata-device.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_ATA_DEVICE_H +#define __FU_ATA_DEVICE_H + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_ATA_DEVICE (fu_ata_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuAtaDevice, fu_ata_device, FU, ATA_DEVICE, FuUdevDevice) + +FuAtaDevice *fu_ata_device_new (FuUdevDevice *device); +FuAtaDevice *fu_ata_device_new_from_blob (const guint8 *buf, + gsize sz, + GError **error); + +/* for self tests */ +guint8 fu_ata_device_get_transfer_mode (FuAtaDevice *self); +guint16 fu_ata_device_get_transfer_blocks (FuAtaDevice *self); + +G_END_DECLS + +#endif /* __FU_ATA_DEVICE_H */ diff --git a/plugins/ata/fu-plugin-ata.c b/plugins/ata/fu-plugin-ata.c new file mode 100644 index 000000000..9a8ab1c34 --- /dev/null +++ b/plugins/ata/fu-plugin-ata.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-ata-device.h" + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + GUdevDevice *udev_device = fu_udev_device_get_dev (device); + g_autoptr(FuAtaDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* interesting device? */ + if (udev_device == NULL) + return TRUE; + if (g_strcmp0 (g_udev_device_get_subsystem (udev_device), "block") != 0) + return TRUE; + if (g_strcmp0 (g_udev_device_get_devtype (udev_device), "disk") != 0) + return TRUE; + if (!g_udev_device_get_property_as_boolean (udev_device, "ID_ATA_SATA")) + return TRUE; + if (!g_udev_device_get_property_as_boolean (udev_device, "ID_ATA_DOWNLOAD_MICROCODE")) + return TRUE; + + dev = fu_ata_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "block"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.t13.ata"); +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, error); +} diff --git a/plugins/ata/fu-self-test.c b/plugins/ata/fu-self-test.c new file mode 100644 index 000000000..02aae8c2e --- /dev/null +++ b/plugins/ata/fu-self-test.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ata-device.h" +#include "fu-test.h" + +static void +fu_ata_id_func (void) +{ + gboolean ret; + gsize sz; + g_autofree gchar *data = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuAtaDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + path = fu_test_get_filename (TESTDATADIR, "StarDrive-SBFM61.2.bin"); + g_assert_nonnull (path); + ret = g_file_get_contents (path, &data, &sz, &error); + g_assert_no_error (error); + g_assert (ret); + dev = fu_ata_device_new_from_blob ((guint8 *)data, sz, &error); + g_assert_no_error (error); + g_assert_nonnull (dev); + g_assert_cmpint (fu_ata_device_get_transfer_mode (dev), ==, 0xe); + g_assert_cmpint (fu_ata_device_get_transfer_blocks (dev), ==, 0x1); + g_assert_cmpstr (fu_device_get_serial (FU_DEVICE (dev)), ==, "A45A078A198600476509"); + g_assert_cmpstr (fu_device_get_name (FU_DEVICE (dev)), ==, "SATA SSD"); + g_assert_cmpstr (fu_device_get_version (FU_DEVICE (dev)), ==, "SBFM61.2"); +} + +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 ("/fwupd/id", fu_ata_id_func); + return g_test_run (); +} diff --git a/plugins/ata/meson.build b/plugins/ata/meson.build new file mode 100644 index 000000000..cd26e395d --- /dev/null +++ b/plugins/ata/meson.build @@ -0,0 +1,58 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginAta"'] + +install_data([ + 'ata.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_ata', + fu_hash, + sources : [ + 'fu-plugin-ata.c', + 'fu-ata-device.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + c_args : [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + link_with : [ + libfwupdprivate, + ], + dependencies : [ + plugin_deps, + ], +) + +if get_option('tests') + testdatadir = join_paths(meson.current_source_dir(), 'tests') + cargs += '-DTESTDATADIR="' + testdatadir + '"' + e = executable( + 'ata-self-test', + sources : [ + 'fu-self-test.c', + 'fu-ata-device.c', + ], + include_directories : [ + include_directories('..'), + include_directories('../..'), + include_directories('../../libfwupd'), + include_directories('../../src'), + ], + dependencies : [ + plugin_deps, + ], + link_with : [ + libfwupdprivate, + ], + c_args : cargs + ) + test('ata-self-test', e) +endif diff --git a/plugins/ata/tests/StarDrive-SBFM61.2.bin b/plugins/ata/tests/StarDrive-SBFM61.2.bin new file mode 100644 index 000000000..201cceca2 Binary files /dev/null and b/plugins/ata/tests/StarDrive-SBFM61.2.bin differ diff --git a/plugins/colorhug/README.md b/plugins/colorhug/README.md index 06eae25d1..4055adcab 100644 --- a/plugins/colorhug/README.md +++ b/plugins/colorhug/README.md @@ -10,3 +10,22 @@ accurate color matching. ColorHug versions 1 and 2 support a custom HID-based flashing protocol, but version 3 (ColorHug+) has now switched to DFU. + +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.hughski.colorhug + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_273F&PID_1001&REV_0001` + * `USB\VID_273F&PID_1001` + * `USB\VID_273F` diff --git a/plugins/colorhug/colorhug.quirk b/plugins/colorhug/colorhug.quirk index da33620d1..81f25f4bc 100644 --- a/plugins/colorhug/colorhug.quirk +++ b/plugins/colorhug/colorhug.quirk @@ -6,6 +6,7 @@ Guid = 40338ceb-b966-4eae-adae-9c32edfcc484 FirmwareSizeMin = 0x2000 FirmwareSizeMax = 0x8000 CounterpartGuid = USB\VID_273F&PID_1001 +InstallDuration = 8 [DeviceInstanceId=USB\VID_273F&PID_1001] Plugin = colorhug @@ -14,6 +15,7 @@ Summary = An open source display colorimeter Icon = colorimeter-colorhug Guid = 40338ceb-b966-4eae-adae-9c32edfcc484 CounterpartGuid = USB\VID_273F&PID_1000 +InstallDuration = 8 # ColorHug2 [DeviceInstanceId=USB\VID_273F&PID_1004] @@ -25,12 +27,14 @@ Guid = 2082b5e0-7a64-478a-b1b2-e3404fab6dad FirmwareSizeMin = 0x2000 FirmwareSizeMax = 0x8000 CounterpartGuid = USB\VID_273F&PID_1005 +InstallDuration = 8 [DeviceInstanceId=USB\VID_273F&PID_1005] Plugin = colorhug Flags = is-bootloader Guid = 2082b5e0-7a64-478a-b1b2-e3404fab6dad CounterpartGuid = USB\VID_273F&PID_1004 +InstallDuration = 8 # ColorHugALS [DeviceInstanceId=USB\VID_273F&PID_1007] @@ -41,9 +45,11 @@ Guid = 84f40464-9272-4ef7-9399-cd95f12da696 FirmwareSizeMin = 0x1000 FirmwareSizeMax = 0x4000 CounterpartGuid = USB\VID_273F&PID_1006 +InstallDuration = 5 [DeviceInstanceId=USB\VID_273F&PID_1006] Plugin = colorhug Flags = halfsize,is-bootloader Guid = 84f40464-9272-4ef7-9399-cd95f12da696 CounterpartGuid = USB\VID_273F&PID_1007 +InstallDuration = 5 diff --git a/plugins/colorhug/fu-colorhug-common.c b/plugins/colorhug/fu-colorhug-common.c index 6bb9b3801..b0773c758 100644 --- a/plugins/colorhug/fu-colorhug-common.c +++ b/plugins/colorhug/fu-colorhug-common.c @@ -85,17 +85,3 @@ ch_strerror (ChError error_enum) return "Self test failed: EEPROM"; return NULL; } - -void -ch_buffer_dump (const gchar *title, const guint8 *buf, gsize sz) -{ - if (g_getenv ("FWUPD_COLORHUG_VERBOSE") == NULL) - return; - g_print ("%s (%" G_GSIZE_FORMAT "):\n", title, sz); - for (gsize i = 0; i < sz; i++) { - g_print ("%02x ", buf[i]); - if (i > 0 && (i + 1) % 256 == 0) - g_print ("\n"); - } - g_print ("\n"); -} diff --git a/plugins/colorhug/fu-colorhug-common.h b/plugins/colorhug/fu-colorhug-common.h index e0cfbf936..e0b2fb9e4 100644 --- a/plugins/colorhug/fu-colorhug-common.h +++ b/plugins/colorhug/fu-colorhug-common.h @@ -52,9 +52,6 @@ typedef enum { } ChError; const gchar *ch_strerror (ChError error_enum); -void ch_buffer_dump (const gchar *title, - const guint8 *buf, - gsize sz); G_END_DECLS diff --git a/plugins/colorhug/fu-colorhug-device.c b/plugins/colorhug/fu-colorhug-device.c index 976ef0e30..7c6922f35 100644 --- a/plugins/colorhug/fu-colorhug-device.c +++ b/plugins/colorhug/fu-colorhug-device.c @@ -82,7 +82,8 @@ fu_colorhug_device_msg (FuColorhugDevice *self, guint8 cmd, memcpy (buf + 1, ibuf, ibufsz); /* request */ - ch_buffer_dump ("REQ", buf, ibufsz + 1); + if (g_getenv ("FWUPD_COLORHUG_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "REQ", buf, ibufsz + 1); if (!g_usb_device_interrupt_transfer (usb_device, CH_USB_HID_EP_OUT, buf, @@ -115,7 +116,8 @@ fu_colorhug_device_msg (FuColorhugDevice *self, guint8 cmd, g_prefix_error (error, "failed to get reply: "); return FALSE; } - ch_buffer_dump ("RES", buf, actual_length); + if (g_getenv ("FWUPD_COLORHUG_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "RES", buf, actual_length); /* old bootloaders do not return the full block */ if (actual_length != CH_USB_HID_EP_SIZE && diff --git a/plugins/colorhug/fu-plugin-colorhug.c b/plugins/colorhug/fu-plugin-colorhug.c index 918617ad7..9aefa6233 100644 --- a/plugins/colorhug/fu-plugin-colorhug.c +++ b/plugins/colorhug/fu-plugin-colorhug.c @@ -13,7 +13,9 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.hughski.colorhug"); } gboolean diff --git a/plugins/colorhug/meson.build b/plugins/colorhug/meson.build index 206e71497..4894f2954 100644 --- a/plugins/colorhug/meson.build +++ b/plugins/colorhug/meson.build @@ -7,6 +7,7 @@ install_data([ ) shared_module('fu_plugin_colorhug', + fu_hash, sources : [ 'fu-colorhug-common.c', 'fu-colorhug-device.c', @@ -19,6 +20,9 @@ shared_module('fu_plugin_colorhug', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/csr/README.md b/plugins/csr/README.md new file mode 100644 index 000000000..2780fe248 --- /dev/null +++ b/plugins/csr/README.md @@ -0,0 +1,36 @@ +CSR Support +=========== + +Introduction +------------ + +CSR is often called “driverless DFU” and is used only by BlueCore chips from +Cambridge Silicon Radio (now owned by Qualcomm). The driverless just means that +it's DFU like, and is routed over HID. + +CSR is a ODM that makes most of the Bluetooth audio chips in vendor hardware. +The hardware vendor can enable or disable features on the CSR microcontroller +depending on licensing options (for instance echo cancellation), and there’s +even a little virtual machine to do simple vendor-specific things. + +All the CSR chips are updatable in-field, and most vendors issue updates to fix +sound quality issues or to add support for new protocols or devices. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +DFU file format. + +This plugin supports the following protocol ID: + + * com.qualcomm.dfu + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_0A12&PID_1337&REV_2520` + * `USB\VID_0A12&PID_1337` + * `USB\VID_0A12` diff --git a/plugins/csr/fu-csr-device.c b/plugins/csr/fu-csr-device.c index eacb8167b..536665545 100644 --- a/plugins/csr/fu-csr-device.c +++ b/plugins/csr/fu-csr-device.c @@ -67,17 +67,6 @@ fu_csr_device_to_string (FuDevice *device, GString *str) g_string_append_printf (str, " timeout:\t\t%" G_GUINT32_FORMAT "\n", self->dnload_timeout); } -static void -fu_csr_device_dump (const gchar *title, const guint8 *buf, gsize sz) -{ - if (g_getenv ("FWUPD_CSR_VERBOSE") == NULL) - return; - g_print ("%s (%" G_GSIZE_FORMAT "):\n", title, sz); - for (gsize i = 0; i < sz; i++) - g_print ("%02x ", buf[i]); - g_print ("\n"); -} - static gboolean fu_csr_device_attach (FuDevice *device, GError **error) { @@ -86,7 +75,8 @@ fu_csr_device_attach (FuDevice *device, GError **error) gsize sz = 0; guint8 buf[] = { FU_CSR_REPORT_ID_CONTROL, FU_CSR_CONTROL_RESET }; - fu_csr_device_dump ("Reset", buf, sz); + if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "Reset", buf, sz); if (!g_usb_device_control_transfer (usb_device, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_CLASS, @@ -135,7 +125,8 @@ fu_csr_device_get_status (FuCsrDevice *self, GError **error) g_prefix_error (error, "Failed to GetStatus: "); return FALSE; } - fu_csr_device_dump ("GetStatus", buf, sz); + if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "GetStatus", buf, sz); /* check packet */ if (sz != FU_CSR_STATUS_HEADER_SIZE) { @@ -179,7 +170,8 @@ fu_csr_device_clear_status (FuCsrDevice *self, GError **error) return TRUE; /* hit hardware */ - fu_csr_device_dump ("ClearStatus", buf, sz); + if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "ClearStatus", buf, sz); if (!g_usb_device_control_transfer (usb_device, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_CLASS, @@ -230,7 +222,8 @@ fu_csr_device_upload_chunk (FuCsrDevice *self, GError **error) g_prefix_error (error, "Failed to ReadFirmware: "); return NULL; } - fu_csr_device_dump ("ReadFirmware", buf, sz); + if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "ReadFirmware", buf, sz); /* too small to parse */ if (sz < FU_CSR_COMMAND_HEADER_SIZE) { @@ -362,7 +355,8 @@ fu_csr_device_download_chunk (FuCsrDevice *self, guint16 idx, GBytes *chunk, GEr memcpy (buf + FU_CSR_COMMAND_HEADER_SIZE, chunk_data, chunk_sz); /* hit hardware */ - fu_csr_device_dump ("Upgrade", buf, sizeof(buf)); + if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "Upgrade", buf, sizeof(buf)); if (!g_usb_device_control_transfer (usb_device, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_CLASS, diff --git a/plugins/csr/fu-plugin-csr.c b/plugins/csr/fu-plugin-csr.c index 3ccc92778..c6e14cea4 100644 --- a/plugins/csr/fu-plugin-csr.c +++ b/plugins/csr/fu-plugin-csr.c @@ -13,7 +13,9 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.qualcomm.dfu"); } gboolean diff --git a/plugins/csr/meson.build b/plugins/csr/meson.build index 401283b20..09595c860 100644 --- a/plugins/csr/meson.build +++ b/plugins/csr/meson.build @@ -5,6 +5,7 @@ install_data(['csr-aiaiai.quirk'], ) shared_module('fu_plugin_csr', + fu_hash, sources : [ 'fu-csr-device.c', 'fu-plugin-csr.c', @@ -22,6 +23,7 @@ shared_module('fu_plugin_csr', plugin_deps, ], link_with : [ + libfwupdprivate, dfu, ], ) diff --git a/plugins/dell-dock/README.md b/plugins/dell-dock/README.md index 101ba4332..e363417da 100644 --- a/plugins/dell-dock/README.md +++ b/plugins/dell-dock/README.md @@ -24,3 +24,48 @@ The MST controller is updated through either the DP Aux interface When this plugin is used, devices present in other plugins may be shown in the topology of this dock. This is intentional as this plugin works together with those plugins to manage the flashing of all components. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract several firmware +blobs with an unspecified binary file format. + +This plugin supports the following protocol ID: + + * com.dell.dock + * com.synaptics.mst + +GUID Generation +--------------- + +These devices use several different generation schemes, e.g. + + * USB Hub1: `USB\VID_413C&PID_B06F&hub` + * USB Hub2: `USB\VID_413C&PID_B06E&hub` + * Embedded Controller: `USB\VID_413C&PID_B06E&hub&embedded` + * Update Level: `USB\VID_413C&PID_B06E&hub&status` + * MST Hub: `MST-panamera-vmm5331-259` + * Thunderbolt Controller: `TBT-00d4b070` + +Custom flag use: +---------------- +This plugin uses the following plugin-specific custom flags: + +* `skip-restart`: Don't run the reset or reboot procedure of the component + +Quirk use +--------- +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|------------------------------|-------------------------------------------------------------------------|-----------------------| +| `DellDockUnlockTarget` | The EC argument needed for unlocking certain device usage. | 1.1.3 | +| `DellDockBlobMajorOffset` | The offset of the major version number in a payload | 1.1.3 | +| `DellDockBlobMinorOffset` | The offset of the minor version number in a payload | 1.1.3 | +| `DellDockBlobBuildOffset` | The offset of the build version number in a payload | 1.1.3 | +| `DellDockBlobVersionOffset` | The offset of the ASCII representation of a version string in a payload | 1.1.3 | +| `DellDockBoardMin` | The minimum board revision required to safely operate the plugin | 1.1.3 | +| `DellDockVersionLowest` | The minimum component version required to safely operate the plugin | 1.1.3 | +| `DellDockBoard*` | The board description of a board revision | 1.1.3 | +| `DellDockInstallDurationI2C` | The duration of time required to install a payload via I2C. | 1.1.3 | diff --git a/plugins/dell-dock/dell-dock.quirk b/plugins/dell-dock/dell-dock.quirk index 5ec659f5d..ce0813c60 100644 --- a/plugins/dell-dock/dell-dock.quirk +++ b/plugins/dell-dock/dell-dock.quirk @@ -70,8 +70,8 @@ FirmwareSizeMax = 0x20000 Flags = require-ac Children = FuDellDockStatus|USB\VID_413C&PID_B06E&hub&status,FuDellDockMst|MST-panamera-vmm5331-259 DellDockUnlockTarget = 1 -DellDockBoardMin = 2 -DellDockVersionLowest = 00.00.00.09 +DellDockBoardMin = 4 +DellDockVersionLowest = 00.00.00.17 DellDockBlobVersionOffset = 0x1AFC0 InstallDuration = 60 @@ -81,7 +81,6 @@ Name = Package level of Dell dock Summary = A representation of dock update status Plugin = dell_dock Vendor = Dell Inc -Flags = updatable FirmwareSizeMin = 24 FirmwareSizeMax = 24 InstallDuration = 5 @@ -114,14 +113,8 @@ FirmwareSizeMin=0x40000 FirmwareSizeMax=0x80000 Flags = require-ac InstallDuration = 22 - -# Thunderbolt controller (old ID) -# TODO: This should be dropped when DellDockBoardMin is 3+ -[Guid=TBT-00d40012] -Name = Thunderbolt controller in Dell dock -Summary = Thunderbolt controller -Vendor = Dell Inc -ParentGuid = USB\VID_413C&PID_B06E&hub&embedded -FirmwareSizeMin=0x40000 -FirmwareSizeMax=0x80000 -Flags = require-ac,ignore-validation +DellDockInstallDurationI2C = 181 +DellDockUnlockTarget = 10 +DellDockHubVersionLowest = 1.31 +DellDockBlobMajorOffset = 0x400a +DellDockBlobMinorOffset = 0x4009 diff --git a/plugins/dell-dock/fu-dell-dock-common.c b/plugins/dell-dock/fu-dell-dock-common.c index d56ac15bb..a3e0836f2 100644 --- a/plugins/dell-dock/fu-dell-dock-common.c +++ b/plugins/dell-dock/fu-dell-dock-common.c @@ -47,10 +47,29 @@ fu_dell_dock_set_power (FuDevice *device, guint8 target, void fu_dell_dock_will_replug (FuDevice *device) { + guint64 timeout = fu_device_get_install_duration (device); + g_return_if_fail (FU_IS_DEVICE (device)); - g_debug ("Activated %ds replug delay for %s", - REPLUG_TIMEOUT, fu_device_get_name (device)); - fu_device_set_remove_delay (device, REPLUG_TIMEOUT * 1000); + g_debug ("Activated %" G_GUINT64_FORMAT "s replug delay for %s", + timeout, fu_device_get_name (device)); + fu_device_set_remove_delay (device, timeout * 1000); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); } + +void +fu_dell_dock_clone_updatable (FuDevice *device) +{ + FuDevice *parent; + parent = fu_device_get_parent (device); + if (parent == NULL) + return; + if (fu_device_has_flag (parent, FWUPD_DEVICE_FLAG_UPDATABLE)) { + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + } else { + const gchar *message = fu_device_get_update_error (parent); + if (message != NULL) + fu_device_set_update_error (device, message); + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + } +} diff --git a/plugins/dell-dock/fu-dell-dock-common.h b/plugins/dell-dock/fu-dell-dock-common.h index 66560b72c..4be3c7733 100644 --- a/plugins/dell-dock/fu-dell-dock-common.h +++ b/plugins/dell-dock/fu-dell-dock-common.h @@ -21,13 +21,13 @@ #include "fu-device.h" #include "fu-dell-dock-i2c-ec.h" #include "fu-dell-dock-i2c-mst.h" +#include "fu-dell-dock-i2c-tbt.h" #include "fu-dell-dock-hub.h" #include "fu-dell-dock-hid.h" #include "fu-dell-dock-status.h" #define DELL_DOCK_EC_GUID "USB\\VID_413C&PID_B06E&hub&embedded" #define DELL_DOCK_TBT_GUID "TBT-00d4b070" -#define REPLUG_TIMEOUT 60 /* s */ gboolean fu_dell_dock_set_power (FuDevice *device, guint8 target, @@ -35,4 +35,6 @@ gboolean fu_dell_dock_set_power (FuDevice *device, GError **error); void fu_dell_dock_will_replug (FuDevice *device); +void fu_dell_dock_clone_updatable (FuDevice *device); + #endif /* __FU_DELL_DOCK_COMMON_H */ diff --git a/plugins/dell-dock/fu-dell-dock-hid.c b/plugins/dell-dock/fu-dell-dock-hid.c index 1dff057af..8cb17367a 100644 --- a/plugins/dell-dock/fu-dell-dock-hid.c +++ b/plugins/dell-dock/fu-dell-dock-hid.c @@ -17,6 +17,7 @@ #include "config.h" #include +#include #include "fu-usb-device.h" #include "fwupd-error.h" @@ -25,6 +26,7 @@ #define HIDI2C_MAX_REGISTER 4 #define HID_MAX_RETRIES 5 +#define TBT_MAX_RETRIES 2 #define HIDI2C_TRANSACTION_TIMEOUT 2000 #define HUB_CMD_READ_DATA 0xC0 @@ -36,6 +38,11 @@ #define HUB_EXT_I2C_READ 0xD6 #define HUB_EXT_VERIFYUPDATE 0xD9 #define HUB_EXT_ERASEBANK 0xE8 +#define HUB_EXT_WRITE_TBT_FLASH 0xFF + +#define TBT_COMMAND_WAKEUP 0x00000000 +#define TBT_COMMAND_AUTHENTICATE 0xFFFFFFFF +#define TBT_COMMAND_AUTHENTICATE_STATUS 0xFFFFFFFE typedef struct __attribute__ ((packed)) { guint8 cmd; @@ -55,6 +62,20 @@ typedef struct __attribute__ ((packed)) { guint8 data[192]; } FuHIDCmdBuffer; +typedef struct __attribute__ ((packed)) { + guint8 cmd; + guint8 ext; + guint8 i2cslaveaddr; + guint8 i2cspeed; + union { + guint32 startaddress; + guint32 tbt_command; + }; + guint8 bufferlen; + guint8 extended_cmdarea[55]; + guint8 data[192]; +} FuTbtCmdBuffer; + static gboolean fu_dell_dock_hid_set_report (FuDevice *self, guint8 *outbuffer, @@ -374,3 +395,142 @@ fu_dell_dock_hid_i2c_read (FuDevice *self, return TRUE; } + +gboolean +fu_dell_dock_hid_tbt_wake (FuDevice *self, + const FuHIDI2CParameters *parameters, + GError **error) +{ + FuTbtCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_READ_DATA, /* special write command that reads status result */ + .ext = HUB_EXT_WRITE_TBT_FLASH, + .i2cslaveaddr = parameters->i2cslaveaddr, + .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ + .tbt_command = TBT_COMMAND_WAKEUP, + .bufferlen = 0, + .extended_cmdarea[0 ... 53] = 0, + .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: "); + return FALSE; + } + if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) { + g_prefix_error (error, "failed to get wake thunderbolt status: "); + return FALSE; + } + g_debug ("thunderbolt wake result: 0x%x", cmd_buffer.data[1]); + + return TRUE; +} + +static const gchar * +fu_dell_dock_hid_tbt_map_error (guint32 code) +{ + if (code == 1) + return g_strerror (EINVAL); + else if (code == 2) + return g_strerror (EPERM); + + return g_strerror (EIO); +} + +gboolean +fu_dell_dock_hid_tbt_write (FuDevice *self, + guint32 start_addr, + const guint8 *input, + gsize write_size, + const FuHIDI2CParameters *parameters, + GError **error) +{ + FuTbtCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */ + .ext = HUB_EXT_WRITE_TBT_FLASH, + .i2cslaveaddr = parameters->i2cslaveaddr, + .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ + .startaddress = GUINT32_TO_LE (start_addr), + .bufferlen = write_size, + .extended_cmdarea[0 ... 53] = 0, + }; + guint8 result; + + g_return_val_if_fail (input != NULL, FALSE); + g_return_val_if_fail (write_size <= HIDI2C_MAX_WRITE, FALSE); + + memcpy (cmd_buffer.data, input, write_size); + + 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: "); + return FALSE; + } + if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) { + g_prefix_error (error, "failed to get TBT flash status: "); + return FALSE; + } + result = cmd_buffer.data[1] & 0xf; + if (result == 0) + break; + g_debug ("attempt %d/%d: Thunderbolt write failed: %x", + i, TBT_MAX_RETRIES, result); + } + if (result != 0) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Writing address 0x%04x failed: %s", + start_addr, fu_dell_dock_hid_tbt_map_error (result)); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_dell_dock_hid_tbt_authenticate (FuDevice *self, + const FuHIDI2CParameters *parameters, + GError **error) +{ + FuTbtCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */ + .ext = HUB_EXT_WRITE_TBT_FLASH, + .i2cslaveaddr = parameters->i2cslaveaddr, + .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ + .tbt_command = GUINT32_TO_LE (TBT_COMMAND_AUTHENTICATE), + .bufferlen = 0, + .extended_cmdarea[0 ... 53] = 0, + }; + guint8 result; + + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, error)) { + g_prefix_error (error, "failed to send authentication: "); + return FALSE; + } + + cmd_buffer.tbt_command = GUINT32_TO_LE (TBT_COMMAND_AUTHENTICATE_STATUS); + /* needs at least 2 seconds */ + g_usleep (2000000); + 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: "); + return FALSE; + } + if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) { + g_prefix_error (error, "failed to get check authentication: "); + return FALSE; + } + result = cmd_buffer.data[1] & 0xf; + if (result == 0) + break; + g_debug ("attempt %d/%d: Thunderbolt authenticate failed: %x", + i, TBT_MAX_RETRIES, result); + g_usleep (500000); + } + if (result != 0) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Thunderbolt authentication failed: %s", + fu_dell_dock_hid_tbt_map_error (result)); + return FALSE; + } + + return TRUE; +} diff --git a/plugins/dell-dock/fu-dell-dock-hid.h b/plugins/dell-dock/fu-dell-dock-hid.h index c732f5992..f69936820 100644 --- a/plugins/dell-dock/fu-dell-dock-hid.h +++ b/plugins/dell-dock/fu-dell-dock-hid.h @@ -78,4 +78,19 @@ gboolean fu_dell_dock_hid_verify_update (FuDevice *self, gboolean *result, GError **error); +gboolean fu_dell_dock_hid_tbt_wake (FuDevice *self, + const FuHIDI2CParameters *parameters, + GError **error); + +gboolean fu_dell_dock_hid_tbt_write (FuDevice *self, + guint32 start_addr, + const guint8 *input, + gsize write_size, + const FuHIDI2CParameters *parameters, + GError **error); + +gboolean fu_dell_dock_hid_tbt_authenticate (FuDevice *self, + const FuHIDI2CParameters *parameters, + GError **error); + #endif /* __FU_DELL_DOCK_HID_H */ diff --git a/plugins/dell-dock/fu-dell-dock-hub.c b/plugins/dell-dock/fu-dell-dock-hub.c index f0f6d0dd1..aa92b7d19 100644 --- a/plugins/dell-dock/fu-dell-dock-hub.c +++ b/plugins/dell-dock/fu-dell-dock-hub.c @@ -15,8 +15,6 @@ #include "config.h" -#include - #include "fu-usb-device.h" #include "fwupd-error.h" diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/plugins/dell-dock/fu-dell-dock-i2c-ec.c index bcacd6b76..7aa9e560e 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -15,9 +15,9 @@ #include "config.h" -#include #include +#include "fu-common-version.h" #include "fu-usb-device.h" #include "fwupd-error.h" @@ -32,14 +32,25 @@ #define EC_CMD_MODIFY_LOCK 0x0a #define EC_CMD_RESET 0x0b #define EC_CMD_REBOOT 0x0c +#define EC_CMD_PASSIVE 0x0d #define EC_GET_FW_UPDATE_STATUS 0x0f #define EXPECTED_DOCK_INFO_SIZE 0xb7 #define EXPECTED_DOCK_TYPE 0x04 +#define TBT_MODE_MASK 0x01 + +#define BIT_SET(x,y) (x |= (1<data->port0_dock_status & TBT_MODE_MASK; + + /* check for TBT module type */ + if (self->data->module_type != MODULE_TYPE_TBT) + return FALSE; + g_debug ("found thunderbolt dock, port mode: %d", port0_tbt_mode); + + return !port0_tbt_mode; +} + +gboolean +fu_dell_dock_ec_tbt_passive (FuDevice *device) { FuDellDockEc *self = FU_DELL_DOCK_EC (device); - return self->data->module_type == MODULE_TYPE_TBT; + if (self->passive_flow > 0) { + self->passive_flow |= PASSIVE_TBT_MASK; + return TRUE; + } + return FALSE; } static const gchar* @@ -332,7 +367,7 @@ fu_dell_dock_ec_get_dock_info (FuDevice *device, device_entry[i].version.version_8[2], device_entry[i].version.version_8[3]); g_debug ("\tParsed version %s", self->ec_version); - fu_device_set_version (self, self->ec_version); + fu_device_set_version (FU_DEVICE (self), self->ec_version); } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_MST) { self->raw_versions->mst_version = device_entry[i].version.version_32; @@ -347,7 +382,8 @@ fu_dell_dock_ec_get_dock_info (FuDevice *device, device_entry[i].version.version_8[2], device_entry[i].version.version_8[3]); g_debug ("\tParsed version %s", self->mst_version); - } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_TBT && fu_dell_dock_ec_has_tbt (device)) { + } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_TBT && + self->data->module_type == MODULE_TYPE_TBT) { /* guard against invalid Thunderbolt version read from EC */ if (!fu_dell_dock_test_valid_byte (device_entry[i].version.version_8, 2)) { g_warning ("[EC bug] EC read invalid Thunderbolt version %08x", @@ -368,14 +404,41 @@ fu_dell_dock_ec_get_dock_info (FuDevice *device, } } + /* Thunderbolt SKU takes a little longer */ + if (self->data->module_type == MODULE_TYPE_TBT) { + guint64 tmp = fu_device_get_install_duration (device); + fu_device_set_install_duration (device, tmp + 20); + } + /* minimum EC version this code will support */ - if (as_utils_vercmp (self->ec_version, self->ec_minimum_version) < 0) { + if (fu_common_vercmp (self->ec_version, self->ec_minimum_version) < 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "dock containing EC version %s is not supported", self->ec_version); return FALSE; } + /* TODO: Drop if setting minimum board to 5+ and minimum EC to 19+ + * If running on board 4 or later, already EC19+, then + * don't allow downgrades to anything < EC19 + */ + if (self->data->board_id >= 4 && + fu_common_vercmp (self->ec_version, "00.00.00.19") >= 0) { + g_debug ("Prohibiting downgrades below EC 00.00.00.19"); + g_free (self->ec_minimum_version); + self->ec_minimum_version = g_strdup ("00.00.00.19"); + } + fu_device_set_version_lowest (device, self->ec_minimum_version); + + + /* TODO: Drop if minimum EC is set to 23+ + * Determine if the passive flow should be used when flashing + */ + if (fu_common_vercmp (self->ec_version, "00.00.00.23") >= 0) { + g_debug ("using passive flow"); + self->passive_flow = PASSIVE_REBOOT_MASK; + fu_device_set_custom_flags (device, "skip-restart"); + } return TRUE; } @@ -390,6 +453,7 @@ fu_dell_dock_ec_get_dock_data (FuDevice *device, const guint8 *result; gsize length = sizeof(FuDellDockDockDataStructure); g_autofree gchar *bundled_serial = NULL; + FuDellDockECFWUpdateStatus status; g_return_val_if_fail (device != NULL, FALSE); @@ -433,11 +497,20 @@ fu_dell_dock_ec_get_dock_data (FuDevice *device, /* 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_get_ec_status (device, &status, error)) + return FALSE; + /* make sure this hardware spin matches our expecations */ if (self->data->board_id >= self->board_min) { - fu_dell_dock_ec_set_board (device); - fu_device_set_version (device, self->ec_version); - fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + if (status != FW_UPDATE_IN_PROGRESS) { + fu_dell_dock_ec_set_board (device); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + } else { + fu_device_set_update_error (device, "An update is pending " + "next time the dock is " + "unplugged"); + } } else { g_warning ("This utility does not support this board, disabling updates for %s", fu_device_get_name (device)); @@ -486,6 +559,7 @@ fu_dell_dock_ec_modify_lock (FuDevice *device, gboolean unlocked, GError **error) { + FuDellDockEc *self = FU_DELL_DOCK_EC (device); guint32 cmd; g_return_val_if_fail (device != NULL, FALSE); @@ -506,6 +580,12 @@ fu_dell_dock_ec_modify_lock (FuDevice *device, fu_device_get_name (device), fu_device_get_id (device)); + if (unlocked) + BIT_SET (self->dock_unlock_status, target); + else + BIT_CLEAR (self->dock_unlock_status, target); + g_debug ("current overall unlock status: 0x%08x", self->dock_unlock_status); + return TRUE; } @@ -523,22 +603,24 @@ gboolean fu_dell_dock_ec_reboot_dock (FuDevice *device, GError **error) { FuDellDockEc *self = FU_DELL_DOCK_EC (device); - guint16 cmd = EC_CMD_REBOOT; g_return_val_if_fail (device != NULL, FALSE); - if (fu_device_has_custom_flag (device, "skip-restart")) { - g_debug ("Skipping reboot per quirk request"); - return TRUE; + if (self->passive_flow > 0) { + guint32 cmd = EC_CMD_PASSIVE | /* cmd */ + 1 << 8 | /* length of data arguments */ + self->passive_flow << 16; + g_debug ("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); + } else { + guint16 cmd = EC_CMD_REBOOT; + g_debug ("rebooting %s", fu_device_get_name (device)); + return fu_dell_dock_ec_write (device, 2, (guint8 *) &cmd, error); } - /* TODO: Drop when bumping minimum EC version to 13+ */ - if (as_utils_vercmp (self->ec_version, "00.00.00.13") < 0) - g_print ("\nEC Reboot API may fail on EC %s. Please manually power cycle dock.\n", - self->ec_version); - g_debug ("Rebooting %s", fu_device_get_name (device)); - - return fu_dell_dock_ec_write (device, 2, (guint8 *) &cmd, error); + return TRUE; } static gboolean @@ -586,6 +668,16 @@ guint32 fu_dell_dock_ec_get_status_version (FuDevice *device) { FuDellDockEc *self = FU_DELL_DOCK_EC (device); + + /* TODO: drop if setting minimum board to 5+ + * this board was manufactured with 89.16.01.00 and won't upgrade + */ + if (self->data->board_id == 4 && + self->raw_versions->pkg_version == 71305) { + g_printerr ("Dock manufactured w/ invalid package %u\n", + self->raw_versions->pkg_version); + self->raw_versions->pkg_version = 0; + } return self->raw_versions->pkg_version; } @@ -617,13 +709,6 @@ fu_dell_dock_ec_commit_package (FuDevice *device, GBytes *blob_fw, g_debug ("\ttbt_version: %x", self->raw_versions->tbt_version); g_debug ("\tpkg_version: %x", self->raw_versions->pkg_version); - /* TODO: Drop when updating minimum EC to 11+ */ - if (as_utils_vercmp (self->ec_version, "00.00.00.11") < 0) { - g_debug ("EC %s doesn't support package version, ignoring", - self->ec_version); - return TRUE; - } - payload [0] = EC_CMD_SET_DOCK_PKG; payload [1] = length; memcpy (payload + 2, data, length); @@ -687,6 +772,13 @@ fu_dell_dock_ec_write_fw (FuDevice *device, GBytes *blob_fw, if (!fu_dell_dock_hid_raise_mcu_clock (self->symbiote, FALSE, error)) return FALSE; + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version (device, dynamic_version); + + /* activate passive behavior */ + if (self->passive_flow) + self->passive_flow |= PASSIVE_RESET_MASK; + if (fu_device_has_custom_flag (device, "skip-restart")) { g_debug ("Skipping EC reset per quirk request"); return TRUE; @@ -723,11 +815,15 @@ fu_dell_dock_ec_write_fw (FuDevice *device, GBytes *blob_fw, error_local->message, status); return TRUE; } + if (status == FW_UPDATE_AUTHENTICATION_FAILED) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid EC firmware image"); + return FALSE; + } } - /* dock will reboot to re-read; this is to appease the daemon */ - fu_device_set_version (device, dynamic_version); - return TRUE; } diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.h b/plugins/dell-dock/fu-dell-dock-i2c-ec.h index a76bcff52..db597c3ff 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.h +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.h @@ -31,7 +31,8 @@ FuDellDockEc *fu_dell_dock_ec_new (FuDevice *symbiote); G_END_DECLS -gboolean fu_dell_dock_ec_has_tbt (FuDevice *device); +gboolean fu_dell_dock_ec_needs_tbt (FuDevice *device); +gboolean fu_dell_dock_ec_tbt_passive (FuDevice *device); gboolean fu_dell_dock_ec_modify_lock (FuDevice *self, guint8 target, gboolean unlocked, diff --git a/plugins/dell-dock/fu-dell-dock-i2c-mst.c b/plugins/dell-dock/fu-dell-dock-i2c-mst.c index 63ce25568..f564b7d6b 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-mst.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-mst.c @@ -16,7 +16,6 @@ #include "config.h" -#include #include #include "fu-common.h" @@ -425,35 +424,6 @@ fu_d19_mst_check_fw (FuDevice *symbiote, GError **error) return TRUE; } -static gboolean -fu_dell_dock_mst_get_version_direct (FuDevice *symbiote, gchar **version_out, - GError **error) -{ - g_autoptr(GBytes) bytes = NULL; - const guint8 *data; - gsize length = 4; - - g_return_val_if_fail (version_out != NULL, FALSE); - - /* Try to read core MCU FW version */ - if (!fu_dell_dock_mst_read_register (symbiote, - MST_CORE_MCU_FW_VERSION, - length, &bytes, - error)) - return FALSE; - data = g_bytes_get_data (bytes, &length); - if (length < 4) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid MST result %" G_GSIZE_FORMAT, length); - return FALSE; - } - *version_out = g_strdup_printf ("%02x.%02x.%02x", - data[1], /* major */ - data[0], /* minor */ - data[2]); /* build */ - return TRUE; -} - static gboolean fu_dell_dock_mst_checksum_bank (FuDevice *symbiote, GBytes *blob_fw, @@ -646,7 +616,7 @@ fu_dell_dock_mst_stop_esm (FuDevice *symbiote, GError **error) if (!fu_dell_dock_mst_rc_command (symbiote, MST_CMD_WRITE_MEMORY, length, MST_REG_HDCP22_DISABLE, - data, + data_out, error)) return FALSE; @@ -772,7 +742,7 @@ fu_dell_dock_mst_write_fw (FuDevice *device, MSTBank bank_in_use = 0; guint retries = 2; gboolean checksum = FALSE; - guint8 order[3] = {Bank0, ESM}; + guint8 order[2] = {ESM, Bank0}; guint16 chip_id; const guint8* data = g_bytes_get_data (blob_fw, NULL); g_autofree gchar *dynamic_version = NULL; @@ -792,7 +762,7 @@ fu_dell_dock_mst_write_fw (FuDevice *device, return FALSE; if (bank_in_use == Bank0) - order[0] = Bank1; + order[1] = Bank1; /* enable remote control */ if (!fu_dell_dock_mst_enable_remote_control (self->symbiote, error)) @@ -924,7 +894,6 @@ fu_dell_dock_mst_setup (FuDevice *device, GError **error) FuDellDockMst *self = FU_DELL_DOCK_MST (device); FuDevice *parent; const gchar *version; - g_autofree gchar *dynamic_version = NULL; /* sanity check that we can talk to MST */ if (!fu_d19_mst_check_fw (self->symbiote, error)) @@ -934,24 +903,17 @@ fu_dell_dock_mst_setup (FuDevice *device, GError **error) parent = fu_device_get_parent (device); version = fu_dell_dock_ec_get_mst_version (parent); - /* TODO: Drop when we can guarantee EC 15+ */ - if (version == NULL) { - if (!fu_dell_dock_mst_get_version_direct (self->symbiote, - &dynamic_version, - error)) - return FALSE; - version = dynamic_version; - } if (version != NULL) fu_device_set_version (device, version); + fu_dell_dock_clone_updatable (device); + return TRUE; } static gboolean fu_dell_dock_mst_probe (FuDevice *device, GError **error) { - fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_set_logical_id (FU_DEVICE (device), "mst"); return TRUE; diff --git a/plugins/dell-dock/fu-dell-dock-i2c-tbt.c b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c new file mode 100644 index 000000000..6d7c53736 --- /dev/null +++ b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2019 Intel Corporation. + * Copyright (C) 2019 Dell Inc. + * All rights reserved. + * + * This software and associated documentation (if any) is furnished + * under a license and may only be used or copied in accordance + * with the terms of the license. + * + * This file is provided under a dual MIT/LGPLv2 license. When using or + * redistributing this file, you may do so under either license. + * Dell Chooses the MIT license part of Dual MIT/LGPLv2 license agreement. + * + * SPDX-License-Identifier: LGPL-2.1+ OR MIT + */ + +#include "config.h" + +#include + +#include "fwupd-error.h" +#include "fu-device.h" +#include "fu-common.h" +#include "fu-common-version.h" + +#include "fu-dell-dock-common.h" + +#define I2C_TBT_ADDRESS 0xa2 + +const FuHIDI2CParameters tbt_base_settings = { + .i2cslaveaddr = I2C_TBT_ADDRESS, + .regaddrlen = 1, + .i2cspeed = I2C_SPEED_400K, +}; + +/* TR Device ID */ +#define PID_OFFSET 0x05 +#define INTEL_PID 0x15ef + +/* earlier versions have bugs */ +#define MIN_NVM "36.01" + +struct _FuDellDockTbt { + FuDevice parent_instance; + FuDevice *symbiote; + guint8 unlock_target; + guint64 blob_major_offset; + guint64 blob_minor_offset; + gchar *hub_minimum_version; +}; + +G_DEFINE_TYPE (FuDellDockTbt, fu_dell_dock_tbt, FU_TYPE_DEVICE) + +static gboolean +fu_dell_dock_tbt_write_fw (FuDevice *device, + GBytes *blob_fw, + GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); + guint32 start_offset = 0; + gsize image_size; + const guint8 *buffer = g_bytes_get_data (blob_fw, &image_size); + guint16 target_system = 0; + g_autoptr(GTimer) timer = g_timer_new (); + g_autofree gchar *dynamic_version = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (blob_fw != NULL, FALSE); + + dynamic_version = g_strdup_printf ("%02x.%02x", + buffer[self->blob_major_offset], + buffer[self->blob_minor_offset]); + g_debug ("writing Thunderbolt firmware version %s", dynamic_version); + g_debug ("Total Image size: %" G_GSIZE_FORMAT, image_size); + + memcpy (&start_offset, buffer, sizeof (guint32)); + g_debug ("Header size 0x%x", start_offset); + if (start_offset > image_size) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, + "Image header is too big (0x%x)", + start_offset); + return FALSE; + } + + memcpy (&target_system, buffer + start_offset + PID_OFFSET, sizeof (guint16)); + if (target_system != INTEL_PID) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, + "Image is not intended for this system (0x%x)", + target_system); + return FALSE; + } + + buffer += start_offset; + image_size -= start_offset; + + g_debug ("waking Thunderbolt controller"); + if (!fu_dell_dock_hid_tbt_wake (self->symbiote, &tbt_base_settings, error)) + return FALSE; + g_usleep (2000000); + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < image_size; i+= HIDI2C_MAX_WRITE, buffer += HIDI2C_MAX_WRITE) { + guint8 write_size = (image_size - i) > HIDI2C_MAX_WRITE ? + HIDI2C_MAX_WRITE : (image_size - i); + + if (!fu_dell_dock_hid_tbt_write (self->symbiote, + i, + buffer, + write_size, + &tbt_base_settings, + error)) + return FALSE; + + fu_device_set_progress_full (device, i, image_size); + } + g_debug ("writing took %f seconds", + g_timer_elapsed (timer, NULL)); + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY); + + if (fu_dell_dock_ec_tbt_passive (fu_device_get_parent (device))) { + g_debug ("using passive flow for Thunderbolt"); + } else if (!fu_dell_dock_hid_tbt_authenticate (self->symbiote, + &tbt_base_settings, + error)) { + g_prefix_error (error, "failed to authenticate: "); + return FALSE; + } + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version (device, dynamic_version); + + return TRUE; +} + +static gboolean +fu_dell_dock_tbt_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); + + if (g_strcmp0 (key, "DellDockUnlockTarget") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp < G_MAXUINT8) { + self->unlock_target = tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid DellDockUnlockTarget"); + return FALSE; + } else if (g_strcmp0 (key, "DellDockInstallDurationI2C") == 0) { + guint64 tmp = fu_common_strtoull (value); + fu_device_set_install_duration (device, tmp); + return TRUE; + } else if (g_strcmp0 (key, "DellDockHubVersionLowest") == 0) { + self->hub_minimum_version = g_strdup (value); + return TRUE; + } else if (g_strcmp0 (key, "DellDockBlobMajorOffset") == 0) { + self->blob_major_offset = fu_common_strtoull (value); + return TRUE; + } else if (g_strcmp0 (key, "DellDockBlobMinorOffset") == 0) { + self->blob_minor_offset = fu_common_strtoull (value); + return TRUE; + } + /* failed */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + + +static gboolean +fu_dell_dock_tbt_setup (FuDevice *device, GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); + 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); + + if (version != NULL) + fu_device_set_version (device, version); + + /* minimum version of NVM that supports this feature */ + if (version == NULL || fu_common_vercmp (version, MIN_NVM) < 0) { + fu_device_set_update_error (device, + "Updates over I2C are disabled due to insuffient NVM version"); + return TRUE; + } + /* minimum Hub2 version that supports this feature */ + hub_version = fu_device_get_version (self->symbiote); + if (fu_common_vercmp (hub_version, self->hub_minimum_version) < 0) { + fu_device_set_update_error (device, + "Updates over I2C are disabled due to insufficient USB 3.1 G2 hub version"); + return TRUE; + } + + fu_dell_dock_clone_updatable (device); + + return TRUE; +} + +static gboolean +fu_dell_dock_tbt_probe (FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent (device); + fu_device_set_physical_id (device, fu_device_get_physical_id (parent)); + fu_device_set_logical_id (FU_DEVICE (device), "tbt"); + fu_device_add_guid (device, DELL_DOCK_TBT_GUID); + + return TRUE; +} + +static gboolean +fu_dell_dock_tbt_open (FuDevice *device, GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); + FuDevice *parent; + + g_return_val_if_fail (self->unlock_target != 0, FALSE); + + parent = fu_device_get_parent (device); + if (parent == NULL) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent"); + return FALSE; + } + + if (self->symbiote == NULL) + self->symbiote = g_object_ref (fu_dell_dock_ec_get_symbiote (parent)); + + if (!fu_device_open (self->symbiote, error)) + return FALSE; + + /* adjust to access controller */ + if (!fu_dell_dock_set_power (device, self->unlock_target, TRUE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_dell_dock_tbt_close (FuDevice *device, GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); + + /* adjust to access controller */ + if (!fu_dell_dock_set_power (device, self->unlock_target, FALSE, error)) + return FALSE; + + return fu_device_close (self->symbiote, error); +} + +static void +fu_dell_dock_tbt_finalize (GObject *object) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT (object); + g_object_unref (self->symbiote); + g_free (self->hub_minimum_version); + + G_OBJECT_CLASS (fu_dell_dock_tbt_parent_class)->finalize (object); +} + +static void +fu_dell_dock_tbt_init (FuDellDockTbt *device) +{} + +static void +fu_dell_dock_tbt_class_init (FuDellDockTbtClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + object_class->finalize = fu_dell_dock_tbt_finalize; + klass_device->probe = fu_dell_dock_tbt_probe; + klass_device->setup = fu_dell_dock_tbt_setup; + klass_device->open = fu_dell_dock_tbt_open; + klass_device->close = fu_dell_dock_tbt_close; + klass_device->write_firmware = fu_dell_dock_tbt_write_fw; + klass_device->set_quirk_kv = fu_dell_dock_tbt_set_quirk_kv; +} + +FuDellDockTbt * +fu_dell_dock_tbt_new (void) +{ + FuDellDockTbt *device = NULL; + device = g_object_new (FU_TYPE_DELL_DOCK_TBT, NULL); + return device; +} diff --git a/plugins/dell-dock/fu-dell-dock-i2c-tbt.h b/plugins/dell-dock/fu-dell-dock-i2c-tbt.h new file mode 100644 index 000000000..62f91c0bd --- /dev/null +++ b/plugins/dell-dock/fu-dell-dock-i2c-tbt.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Intel Corporation. + * Copyright (C) 2019 Dell Inc. + * All rights reserved. + * + * This software and associated documentation (if any) is furnished + * under a license and may only be used or copied in accordance + * with the terms of the license. + * + * This file is provided under a dual MIT/LGPLv2 license. When using or + * redistributing this file, you may do so under either license. + * Dell Chooses the MIT license part of Dual MIT/LGPLv2 license agreement. + * + * SPDX-License-Identifier: LGPL-2.1+ OR MIT + */ + +#ifndef __FU_DELLDOCK_I2C_TBT_H +#define __FU_DELLDOCK_I2C_TBT_H + +#include "config.h" + +#include "fu-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_DELL_DOCK_TBT (fu_dell_dock_tbt_get_type ()) +G_DECLARE_FINAL_TYPE (FuDellDockTbt, fu_dell_dock_tbt, FU, DELL_DOCK_TBT, FuDevice) + +FuDellDockTbt *fu_dell_dock_tbt_new (void); + +G_END_DECLS + + +#endif /* __FU_DELLDOCK_I2C_TBT_H */ diff --git a/plugins/dell-dock/fu-dell-dock-status.c b/plugins/dell-dock/fu-dell-dock-status.c index 478ed0a08..e28ee54b7 100644 --- a/plugins/dell-dock/fu-dell-dock-status.c +++ b/plugins/dell-dock/fu-dell-dock-status.c @@ -16,7 +16,6 @@ #include "config.h" #include -#include #include "fu-dell-dock-common.h" @@ -52,6 +51,8 @@ fu_dell_dock_status_setup (FuDevice *device, GError **error) fu_device_set_version (device, dynamic_version); fu_device_set_logical_id (FU_DEVICE (device), "status"); + fu_dell_dock_clone_updatable (device); + return TRUE; } diff --git a/plugins/dell-dock/fu-plugin-dell-dock.c b/plugins/dell-dock/fu-plugin-dell-dock.c index a8845e1a7..4cda82707 100644 --- a/plugins/dell-dock/fu-plugin-dell-dock.c +++ b/plugins/dell-dock/fu-plugin-dell-dock.c @@ -21,8 +21,11 @@ #include "fu-dell-dock-common.h" -void fu_plugin_init (FuPlugin *plugin) +void +fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + /* allow these to be built by quirks */ fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); g_type_ensure (FU_TYPE_DELL_DOCK_STATUS); @@ -30,6 +33,8 @@ void fu_plugin_init (FuPlugin *plugin) /* currently slower performance, but more reliable in corner cases */ fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "synapticsmst"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.dell.dock"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.synaptics.mst"); } static gboolean @@ -63,6 +68,16 @@ fu_plugin_dell_dock_probe (FuPlugin *plugin, error)) return FALSE; + /* create TBT endpoint if Thunderbolt SKU and Thunderbolt link inactive */ + if (fu_dell_dock_ec_needs_tbt (FU_DEVICE (ec_device))) { + g_autoptr(FuDellDockTbt) tbt_device = fu_dell_dock_tbt_new (); + fu_device_add_child (FU_DEVICE (ec_device), FU_DEVICE (tbt_device)); + if (!fu_plugin_dell_dock_create_node (plugin, + FU_DEVICE (tbt_device), + error)) + return FALSE; + } + return TRUE; } @@ -102,6 +117,9 @@ fu_plugin_usb_device_added (FuPlugin *plugin, } } + /* clear updatable flag if parent doesn't have it */ + fu_dell_dock_clone_updatable (fu_device); + return TRUE; } diff --git a/plugins/dell-dock/meson.build b/plugins/dell-dock/meson.build index 214420b50..0f00003d3 100644 --- a/plugins/dell-dock/meson.build +++ b/plugins/dell-dock/meson.build @@ -5,6 +5,7 @@ install_data(['dell-dock.quirk'], ) shared_module('fu_plugin_dell_dock', + fu_hash, sources : [ 'fu-plugin-dell-dock.c', 'fu-dell-dock-common.c', @@ -12,6 +13,7 @@ shared_module('fu_plugin_dell_dock', 'fu-dell-dock-status.c', 'fu-dell-dock-i2c-ec.c', 'fu-dell-dock-hub.c', + 'fu-dell-dock-i2c-tbt.c', 'fu-dell-dock-i2c-mst.c' ], include_directories : [ @@ -21,6 +23,9 @@ shared_module('fu_plugin_dell_dock', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/dell-esrt/README.md b/plugins/dell-esrt/README.md index 0a0f9dd68..797558a14 100644 --- a/plugins/dell-esrt/README.md +++ b/plugins/dell-esrt/README.md @@ -7,6 +7,11 @@ Introduction This allows enabling the BIOS setup option for UEFI capsule updates without manually going into BIOS setup. +GUID Generation +--------------- + +These device uses a hardcoded GUID of `2d47f29b-83a2-4f31-a2e8-63474f4d4c2e`. + Build Requirements ------------------ diff --git a/plugins/dell-esrt/fu-plugin-dell-esrt.c b/plugins/dell-esrt/fu-plugin-dell-esrt.c index 7e32b4f59..a13d78c70 100644 --- a/plugins/dell-esrt/fu-plugin-dell-esrt.c +++ b/plugins/dell-esrt/fu-plugin-dell-esrt.c @@ -83,6 +83,12 @@ fu_plugin_dell_esrt_admin_password_present (gboolean *password_present, GError * return TRUE; } +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + gboolean fu_plugin_startup (FuPlugin *plugin, GError **error) { diff --git a/plugins/dell-esrt/meson.build b/plugins/dell-esrt/meson.build index 76da59ea4..45af43ee3 100644 --- a/plugins/dell-esrt/meson.build +++ b/plugins/dell-esrt/meson.build @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginDellEsrt"'] shared_module('fu_plugin_dell_esrt', + fu_hash, sources : [ 'fu-plugin-dell-esrt.c', ], @@ -12,8 +13,11 @@ shared_module('fu_plugin_dell_esrt', install : true, install_dir: plugin_dir, c_args : [ - cargs, - ], + cargs, + ], + link_with : [ + libfwupdprivate, + ], dependencies : [ plugin_deps, libsmbios_c, diff --git a/plugins/dell/README.md b/plugins/dell/README.md index 292b64688..eb54bcfa0 100644 --- a/plugins/dell/README.md +++ b/plugins/dell/README.md @@ -6,6 +6,16 @@ Introduction This allows installing Dell capsules that are not part of the ESRT table. +GUID Generation +--------------- + +These devices uses custom GUIDs for Dell-specific hardware. + + * Thunderbolt devices: `TBT-0x00d4u$(system-id)` + * TPM devices `$(system-id)-$(mode)`, where `mode` is either `2.0` or `1.2` + +In both cases the `system-id` is derived from the SMBIOS Product SKU property. + Build Requirements ------------------ diff --git a/plugins/dell/dell.quirk b/plugins/dell/dell.quirk index d6c1c43f6..3a037e3a2 100644 --- a/plugins/dell/dell.quirk +++ b/plugins/dell/dell.quirk @@ -38,10 +38,3 @@ UefiVersionFormat = quad [SmbiosManufacturer=Alienware] UefiVersionFormat = quad - -# DEPRECATED: put this in the AppStream metadata itself using -# -# quad -# -[DaemonVersionFormat=quad] -ComponentIDs = com.dell.uefi*.firmware diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 707aef12d..a10c841ee 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -13,6 +13,7 @@ #include #include +#include "fu-common-guid.h" #include "fu-plugin-dell.h" #include "fu-plugin-vfuncs.h" #include "fu-device-metadata.h" @@ -44,6 +45,16 @@ typedef struct _DOCK_DESCRIPTION const gchar * desc; } DOCK_DESCRIPTION; +struct da_structure { + guint8 type; + guint8 length; + guint16 handle; + guint16 cmd_address; + guint8 cmd_code; + guint32 supported_cmds; + guint8 *tokens; +} __attribute__((packed)); + /* These are for matching the components */ #define WD15_EC_STR "2 0 2 2 0" #define TB16_EC_STR "2 0 2 1 0" @@ -53,6 +64,8 @@ typedef struct _DOCK_DESCRIPTION #define LEGACY_CBL_STR "2 2 2 1 0" #define UNIV_CBL_STR "2 2 2 2 0" #define TBT_CBL_STR "2 2 2 3 0" +#define FUTURE_EC_STR "3 0 2 4 0" +#define FUTURE_EC_STR2 "4 0 2 4 0" /* supported dock related GUIDs */ #define DOCK_FLASH_GUID "e7ca1f36-bf73-4574-afe6-a4ccacabf479" @@ -157,8 +170,10 @@ static gboolean fu_dell_supported (FuPlugin *plugin) { GBytes *de_table = NULL; + GBytes *da_table = NULL; GBytes *enclosure = NULL; const guint8 *value; + const struct da_structure *da_values; gsize len; /* make sure that Dell SMBIOS methods are available */ @@ -170,6 +185,17 @@ fu_dell_supported (FuPlugin *plugin) return FALSE; if (*value != 0xDE) return FALSE; + da_table = fu_plugin_get_smbios_data (plugin, 0xDA); + if (da_table == NULL) + return FALSE; + da_values = (struct da_structure *) g_bytes_get_data (da_table, &len); + if (len == 0) + return FALSE; + if (!(da_values->supported_cmds & (1 << DACI_FLASH_INTERFACE_CLASS))) { + g_debug ("unable to access flash interface. supported commands: 0x%x", + da_values->supported_cmds); + return FALSE; + } /* only run on intended Dell hw types */ enclosure = fu_plugin_get_smbios_data (plugin, @@ -201,6 +227,8 @@ fu_plugin_dell_match_dock_component (const gchar *query_str, {TBT_CBL_GUID, TBT_CBL_STR, TBT_CBL_DESC}, {UNIV_CBL_GUID, UNIV_CBL_STR, UNIV_CBL_DESC}, {LEGACY_CBL_GUID, LEGACY_CBL_STR, LEGACY_CBL_DESC}, + {NULL, FUTURE_EC_STR, NULL}, + {NULL, FUTURE_EC_STR2, NULL}, }; for (guint i = 0; i < G_N_ELEMENTS (list); i++) { @@ -231,7 +259,7 @@ fu_plugin_dell_inject_fake_data (FuPlugin *plugin, data->can_switch_modes = TRUE; } -static AsVersionParseFlag +static FuVersionFormat fu_plugin_dell_get_version_format (FuPlugin *plugin) { const gchar *content; @@ -240,17 +268,15 @@ fu_plugin_dell_get_version_format (FuPlugin *plugin) content = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); if (content == NULL) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + return FU_VERSION_FORMAT_TRIPLET; /* any quirks match */ group = g_strdup_printf ("SmbiosManufacturer=%s", content); quirk = fu_plugin_lookup_quirk_by_id (plugin, group, FU_QUIRKS_UEFI_VERSION_FORMAT); - if (g_strcmp0 (quirk, "quad") == 0) - return AS_VERSION_PARSE_FLAG_NONE; - - /* fall back */ - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + if (quirk == NULL) + return FU_VERSION_FORMAT_TRIPLET; + return fu_common_version_format_from_string (quirk); } static gboolean @@ -318,7 +344,7 @@ fu_plugin_usb_device_added (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); - AsVersionParseFlag parse_flags; + FuVersionFormat version_format; guint16 pid; guint16 vid; const gchar *query_str; @@ -371,7 +397,7 @@ fu_plugin_usb_device_added (FuPlugin *plugin, g_debug ("Dock cable type: %" G_GUINT32_FORMAT, dock_info->cable_type); g_debug ("Dock location: %d", dock_info->location); g_debug ("Dock component count: %d", dock_info->component_count); - parse_flags = fu_plugin_dell_get_version_format (plugin); + version_format = fu_plugin_dell_get_version_format (plugin); for (guint i = 0; i < dock_info->component_count; i++) { g_autofree gchar *fw_str = NULL; @@ -396,6 +422,10 @@ fu_plugin_usb_device_added (FuPlugin *plugin, "invalid dock component request %s", query_str); return FALSE; } + if (component_guid == NULL || component_name == NULL) { + g_debug ("%s is supported by another plugin", query_str); + return TRUE; + } /* dock EC hasn't been updated for first time */ if (dock_info->flash_pkg_version == 0x00ffffff) { @@ -410,8 +440,8 @@ fu_plugin_usb_device_added (FuPlugin *plugin, continue; } - fw_str = as_utils_version_from_uint32 (dock_info->components[i].fw_version, - parse_flags); + fw_str = fu_common_version_from_uint32 (dock_info->components[i].fw_version, + version_format); if (!fu_plugin_dock_node (plugin, platform, buf.record->dock_info_header.dock_type, @@ -426,8 +456,8 @@ fu_plugin_usb_device_added (FuPlugin *plugin, /* if an old EC or invalid EC version found, create updatable parent */ if (old_ec) - flash_ver_str = as_utils_version_from_uint32 (dock_info->flash_pkg_version, - parse_flags); + flash_ver_str = fu_common_version_from_uint32 (dock_info->flash_pkg_version, + version_format); if (!fu_plugin_dock_node (plugin, platform, buf.record->dock_info_header.dock_type, @@ -597,17 +627,17 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) } tpm_guid_raw = g_strdup_printf ("%04x-%s", system_id, tpm_mode); - tpm_guid = as_utils_guid_from_string (tpm_guid_raw); + tpm_guid = fu_common_guid_from_string (tpm_guid_raw); tpm_id = g_strdup_printf ("DELL-%s" G_GUINT64_FORMAT, tpm_guid); tpm_guid_raw_alt = g_strdup_printf ("%04x-%s", system_id, tpm_mode_alt); - tpm_guid_alt = as_utils_guid_from_string (tpm_guid_raw_alt); + tpm_guid_alt = fu_common_guid_from_string (tpm_guid_raw_alt); tpm_id_alt = g_strdup_printf ("DELL-%s" G_GUINT64_FORMAT, tpm_guid_alt); g_debug ("Creating primary TPM GUID %s and secondary TPM GUID %s", tpm_guid_raw, tpm_guid_raw_alt); - version_str = as_utils_version_from_uint32 (out->fw_version, - AS_VERSION_PARSE_FLAG_NONE); + version_str = fu_common_version_from_uint32 (out->fw_version, + FU_VERSION_FORMAT_QUAD); /* make it clear that the TPM is a discrete device of the product */ if (!data->smi_obj->fake_smbios) { @@ -787,6 +817,7 @@ fu_plugin_init (FuPlugin *plugin) FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); g_autofree gchar *tmp = NULL; + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); tmp = g_strdup_printf ("%d.%d", smbios_get_library_version_major(), smbios_get_library_version_minor()); diff --git a/plugins/dell/fu-self-test.c b/plugins/dell/fu-self-test.c index ff07c4bcd..083b0b683 100644 --- a/plugins/dell/fu-self-test.c +++ b/plugins/dell/fu-self-test.c @@ -69,7 +69,7 @@ fu_plugin_dell_tpm_func (void) struct tpm_status tpm_out; g_autoptr(FuPlugin) plugin_dell = NULL; g_autoptr(FuPlugin) plugin_uefi = NULL; - g_autoptr(GBytes) blob_fw = g_bytes_new_static ("fw", 2); + g_autoptr(GBytes) blob_fw = g_bytes_new_static ("fw", 30); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; @@ -244,8 +244,11 @@ fu_plugin_dell_tpm_func (void) g_clear_error (&error); /* test override */ + g_test_expect_message ("FuPluginUefi", G_LOG_LEVEL_WARNING, + "missing or invalid embedded capsule header"); ret = fu_plugin_runner_update (plugin_uefi, device_v20, NULL, blob_fw, FWUPD_INSTALL_FLAG_FORCE, &error); + g_test_assert_expected_messages (); g_assert_no_error (error); g_assert (ret); } diff --git a/plugins/dell/meson.build b/plugins/dell/meson.build index 0a0dcc5df..df96b979f 100644 --- a/plugins/dell/meson.build +++ b/plugins/dell/meson.build @@ -5,6 +5,7 @@ install_data(['dell.quirk'], ) shared_module('fu_plugin_dell', + fu_hash, sources : [ 'fu-plugin-dell.c', 'fu-dell-smi.c', @@ -16,9 +17,12 @@ shared_module('fu_plugin_dell', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : [ - cargs, - ], + cargs, + ], dependencies : [ plugin_deps, efivar, @@ -51,7 +55,6 @@ if get_option('tests') valgrind, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : [ diff --git a/plugins/dfu/README.md b/plugins/dfu/README.md index 4a408f90a..f251486ae 100644 --- a/plugins/dfu/README.md +++ b/plugins/dfu/README.md @@ -6,3 +6,33 @@ Introduction Device Firmware Update is a standard that allows USB devices to be easily and safely updated by any operating system. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +DFU or DfuSe file format. + +This plugin supports the following protocol IDs: + + * org.usb.dfu + * com.st.dfuse + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_273F&PID_1003&REV_0001` + * `USB\VID_273F&PID_1003` + * `USB\VID_273F` + +Quirk use +--------- +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|------------------------|---------------------------------------------|-----------------------| +|`DfuFlags` | Optional quirks for a DFU device which doesn't follow the DFU 1.0 or 1.1 specification | 1.0.1| +|`DfuForceVersion` | Forces a specific DFU version for the hardware device. This is required if the device does not set, or sets incorrectly, items in the DFU functional descriptor. |1.0.1| +|`DfuJabraDetach` | Assigns the two magic bytes sent to the Jabra hardware when the device is in runtime mode to make it switch into DFU mode.|1.0.1| diff --git a/plugins/dfu/dfu-common.c b/plugins/dfu/dfu-common.c index fd5173cfb..9e747872c 100644 --- a/plugins/dfu/dfu-common.c +++ b/plugins/dfu/dfu-common.c @@ -326,3 +326,27 @@ dfu_utils_buffer_parse_uint32 (const gchar *data) buffer[8] = '\0'; return (guint32) g_ascii_strtoull (buffer, NULL, 16); } + +/** + * dfu_utils_strnsplit: + * @str: a string to split + * @sz: size of @str + * @delimiter: a string which specifies the places at which to split the string + * @max_tokens: the maximum number of pieces to split @str into + * + * Splits a string into a maximum of @max_tokens pieces, using the given + * delimiter. If @max_tokens is reached, the remainder of string is appended + * to the last token. + * + * Return value: a newly-allocated NULL-terminated array of strings + **/ +gchar ** +dfu_utils_strnsplit (const gchar *str, gsize sz, + const gchar *delimiter, gint max_tokens) +{ + if (str[sz - 1] != '\0') { + g_autofree gchar *str2 = g_strndup (str, sz); + return g_strsplit (str2, delimiter, max_tokens); + } + return g_strsplit (str, delimiter, max_tokens); +} diff --git a/plugins/dfu/dfu-common.h b/plugins/dfu/dfu-common.h index b51dfadec..09c54a97c 100644 --- a/plugins/dfu/dfu-common.h +++ b/plugins/dfu/dfu-common.h @@ -165,6 +165,10 @@ guint8 dfu_utils_buffer_parse_uint8 (const gchar *data); guint16 dfu_utils_buffer_parse_uint16 (const gchar *data); guint32 dfu_utils_buffer_parse_uint24 (const gchar *data); guint32 dfu_utils_buffer_parse_uint32 (const gchar *data); +gchar **dfu_utils_strnsplit (const gchar *str, + gsize sz, + const gchar *delimiter, + gint max_tokens); G_END_DECLS diff --git a/plugins/dfu/dfu-device.c b/plugins/dfu/dfu-device.c index 33001e17c..9142fcaee 100644 --- a/plugins/dfu/dfu-device.c +++ b/plugins/dfu/dfu-device.c @@ -578,6 +578,7 @@ dfu_device_set_quirks_from_string (DfuDevice *device, const gchar *str) { DfuDevicePrivate *priv = GET_PRIVATE (device); g_auto(GStrv) split = g_strsplit (str, ",", -1); + priv->quirks = DFU_DEVICE_QUIRK_NONE; for (guint i = 0; split[i] != NULL; i++) { if (g_strcmp0 (split[i], "ignore-polltimeout") == 0) { priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT; @@ -1442,9 +1443,6 @@ dfu_device_close (FuUsbDevice *device, GError **error) DfuDevicePrivate *priv = GET_PRIVATE (self); GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); - /* unset the quirks */ - priv->quirks = DFU_DEVICE_QUIRK_NONE; - /* release interface */ if (priv->claimed_interface) { g_usb_device_release_interface (usb_device, diff --git a/plugins/dfu/dfu-firmware.c b/plugins/dfu/dfu-firmware.c index 1b56afd42..1b8b5111c 100644 --- a/plugins/dfu/dfu-firmware.c +++ b/plugins/dfu/dfu-firmware.c @@ -21,7 +21,8 @@ #include #include -#include + +#include "fu-common-version.h" #include "dfu-common.h" #include "dfu-firmware.h" @@ -642,8 +643,8 @@ dfu_firmware_to_string (DfuFirmware *firmware) g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); - release_str = as_utils_version_from_uint16 (priv->release, - AS_VERSION_PARSE_FLAG_USE_BCD); + release_str = fu_common_version_from_uint16 (priv->release, + FU_VERSION_FORMAT_BCD); str = g_string_new (""); g_string_append_printf (str, "vid: 0x%04x\n", priv->vid); g_string_append_printf (str, "pid: 0x%04x\n", priv->pid); diff --git a/plugins/dfu/dfu-format-ihex.c b/plugins/dfu/dfu-format-ihex.c index 3d3c9a7ca..35acaa393 100644 --- a/plugins/dfu/dfu-format-ihex.c +++ b/plugins/dfu/dfu-format-ihex.c @@ -33,19 +33,47 @@ dfu_firmware_detect_ihex (GBytes *bytes) data = (guint8 *) g_bytes_get_data (bytes, &len); if (len < 12) return DFU_FIRMWARE_FORMAT_UNKNOWN; - if (data[0] != ':') - return DFU_FIRMWARE_FORMAT_UNKNOWN; - return DFU_FIRMWARE_FORMAT_INTEL_HEX; + + /* match the first char */ + if (data[0] == ':') + return DFU_FIRMWARE_FORMAT_INTEL_HEX; + + /* look for the EOF line */ + if (g_strstr_len ((const gchar *) data, (gssize) len, ":000000") != NULL) + return DFU_FIRMWARE_FORMAT_INTEL_HEX; + + /* failed */ + return DFU_FIRMWARE_FORMAT_UNKNOWN; } #define DFU_INHX32_RECORD_TYPE_DATA 0x00 #define DFU_INHX32_RECORD_TYPE_EOF 0x01 #define DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT 0x02 #define DFU_INHX32_RECORD_TYPE_START_SEGMENT 0x03 -#define DFU_INHX32_RECORD_TYPE_EXTENDED 0x04 -#define DFU_INHX32_RECORD_TYPE_ADDR32 0x05 +#define DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR 0x04 +#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05 #define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd +static const gchar * +dfu_firmware_ihex_record_type_to_string (guint8 record_type) +{ + if (record_type == DFU_INHX32_RECORD_TYPE_DATA) + return "DATA"; + if (record_type == DFU_INHX32_RECORD_TYPE_EOF) + return "EOF"; + if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT) + return "EXTENDED_SEGMENT"; + if (record_type == DFU_INHX32_RECORD_TYPE_START_SEGMENT) + return "START_SEGMENT"; + if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR) + return "EXTENDED_LINEAR"; + if (record_type == DFU_INHX32_RECORD_TYPE_START_LINEAR) + return "ADDR32"; + if (record_type == DFU_INHX32_RECORD_TYPE_SIGNATURE) + return "SIGNATURE"; + return NULL; +} + /** * dfu_firmware_from_ihex: (skip) * @firmware: a #DfuFirmware @@ -63,25 +91,19 @@ dfu_firmware_from_ihex (DfuFirmware *firmware, DfuFirmwareParseFlags flags, GError **error) { - const gchar *in_buffer; + const gchar *data; gboolean got_eof = FALSE; - gsize len_in; - guint16 addr_high = 0; - guint16 addr_low = 0; - guint32 addr32 = 0; - guint32 addr32_last = 0; - guint32 element_address = 0; - guint8 checksum; - guint8 data_tmp; - guint8 len_tmp; - guint8 type; - guint end; - guint offset = 0; + gsize sz = 0; + guint32 abs_addr = 0x0; + guint32 addr_last = 0x0; + guint32 base_addr = 0x0; + guint32 seg_addr = 0x0; + g_auto(GStrv) lines = NULL; g_autoptr(DfuElement) element = NULL; g_autoptr(DfuImage) image = NULL; g_autoptr(GBytes) contents = NULL; - g_autoptr(GString) string = NULL; - g_autoptr(GString) signature = g_string_new (NULL); + g_autoptr(GString) buf = g_string_new (NULL); + g_autoptr(GString) buf_signature = g_string_new (NULL); g_return_val_if_fail (bytes != NULL, FALSE); @@ -91,95 +113,110 @@ dfu_firmware_from_ihex (DfuFirmware *firmware, element = dfu_element_new (); /* parse records */ - in_buffer = g_bytes_get_data (bytes, &len_in); - string = g_string_new (""); - while (offset < len_in) { + data = g_bytes_get_data (bytes, &sz); + lines = dfu_utils_strnsplit (data, sz, "\n", -1); + for (guint ln = 0; lines[ln] != NULL; ln++) { + const gchar *line = lines[ln]; + gsize linesz; + guint32 addr; + guint8 byte_cnt; + guint8 record_type; + guint line_end; + + /* ignore comments */ + if (g_str_has_prefix (line, ";")) + continue; + + /* ignore blank lines */ + g_strdelimit (lines[ln], "\r\x1a", '\0'); + linesz = strlen (line); + if (linesz == 0) + continue; /* check starting token */ - if (in_buffer[offset] != ':') { + if (line[0] != ':') { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid starting token, got %c at %x", - in_buffer[offset], offset); + "invalid starting token on line %u: %s", + ln + 1, line); return FALSE; } /* check there's enough data for the smallest possible record */ - if (offset + 12 > (guint) len_in) { + if (linesz < 11) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "record incomplete at %u, length %u", - offset, (guint) len_in); + "line %u is incomplete, length %u", + ln + 1, (guint) linesz); return FALSE; } /* length, 16-bit address, type */ - len_tmp = dfu_utils_buffer_parse_uint8 (in_buffer + offset + 1); - addr_low = dfu_utils_buffer_parse_uint16 (in_buffer + offset + 3); - type = dfu_utils_buffer_parse_uint8 (in_buffer + offset + 7); + byte_cnt = dfu_utils_buffer_parse_uint8 (line + 1); + addr = dfu_utils_buffer_parse_uint16 (line + 3); + record_type = dfu_utils_buffer_parse_uint8 (line + 7); + g_debug ("%s:", dfu_firmware_ihex_record_type_to_string (record_type)); + g_debug (" addr_start:\t0x%04x", addr); + g_debug (" length:\t0x%02x", byte_cnt); + addr += seg_addr; + addr += abs_addr; + g_debug (" addr:\t0x%08x", addr); /* position of checksum */ - end = offset + 9 + len_tmp * 2; - if (end > (guint) len_in) { + line_end = 9 + byte_cnt * 2; + if (line_end > (guint) linesz) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "checksum > file length: %u", - end); + "line %u malformed, length: %u", + ln + 1, line_end); return FALSE; } /* verify checksum */ if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) { - checksum = 0; - for (guint i = offset + 1; i < end + 2; i += 2) { - data_tmp = dfu_utils_buffer_parse_uint8 (in_buffer + i); + guint8 checksum = 0; + for (guint i = 1; i < line_end + 2; i += 2) { + guint8 data_tmp = dfu_utils_buffer_parse_uint8 (line + i); checksum += data_tmp; } if (checksum != 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid record checksum at 0x%04x " - "to 0x%04x, got 0x%02x", - offset, end, checksum); + "line %u has invalid checksum (0x%02x)", + ln + 1, checksum); return FALSE; } } /* process different record types */ - switch (type) { + switch (record_type) { case DFU_INHX32_RECORD_TYPE_DATA: - /* if not contiguous with previous record */ - if ((addr_high + addr_low) != addr32) { - if (addr32 == 0x0) { - g_debug ("base address %08x", addr_low); - dfu_element_set_address (element, addr_low); - } - addr32 = ((guint32) addr_high << 16) + addr_low; - if (element_address == 0x0) - element_address = addr32; - } + /* base address for element */ + if (base_addr == 0x0) + base_addr = addr; /* does not make sense */ - if (addr32 < addr32_last) { + if (addr < addr_last) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "invalid address 0x%x, last was 0x%x", - (guint) addr32, - (guint) addr32_last); + (guint) addr, + (guint) addr_last); return FALSE; } /* parse bytes from line */ - g_debug ("writing data 0x%08x", (guint32) addr32); - for (guint i = offset + 9; i < end; i += 2) { + g_debug ("writing data 0x%08x", (guint32) addr); + for (guint i = 9; i < line_end; i += 2) { /* any holes in the hex record */ - guint32 len_hole = addr32 - addr32_last; - if (addr32_last > 0 && len_hole > 0x100000) { + guint32 len_hole = addr - addr_last; + guint8 data_tmp; + if (addr_last > 0 && len_hole > 0x100000) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -187,19 +224,19 @@ dfu_firmware_from_ihex (DfuFirmware *firmware, (guint) len_hole); return FALSE; } - if (addr32_last > 0x0 && len_hole > 1) { + if (addr_last > 0x0 && len_hole > 1) { + g_debug ("filling address 0x%08x to 0x%08x", + addr_last + 1, addr_last + len_hole - 1); for (guint j = 1; j < len_hole; j++) { - g_debug ("filling address 0x%08x", - addr32_last + j); /* although 0xff might be clearer, * we can't write 0xffff to pic14 */ - g_string_append_c (string, 0x00); + g_string_append_c (buf, 0x00); } } /* write into buf */ - data_tmp = dfu_utils_buffer_parse_uint8 (in_buffer + i); - g_string_append_c (string, (gchar) data_tmp); - addr32_last = addr32++; + data_tmp = dfu_utils_buffer_parse_uint8 (line + i); + g_string_append_c (buf, (gchar) data_tmp); + addr_last = addr++; } break; case DFU_INHX32_RECORD_TYPE_EOF: @@ -213,25 +250,28 @@ dfu_firmware_from_ihex (DfuFirmware *firmware, } got_eof = TRUE; break; - case DFU_INHX32_RECORD_TYPE_EXTENDED: - addr_high = dfu_utils_buffer_parse_uint16 (in_buffer + offset + 9); - addr32 = ((guint32) addr_high << 16) + addr_low; + case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR: + abs_addr = dfu_utils_buffer_parse_uint16 (line + 9) << 16; + g_debug (" abs_addr:\t0x%02x", abs_addr); break; - case DFU_INHX32_RECORD_TYPE_ADDR32: - addr32 = dfu_utils_buffer_parse_uint32 (in_buffer + offset + 9); + case DFU_INHX32_RECORD_TYPE_START_LINEAR: + abs_addr = dfu_utils_buffer_parse_uint32 (line + 9); + g_debug (" abs_addr:\t0x%08x", abs_addr); break; case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT: /* segment base address, so ~1Mb addressable */ - addr32 = dfu_utils_buffer_parse_uint16 (in_buffer + offset + 9) * 16; + seg_addr = dfu_utils_buffer_parse_uint16 (line + 9) * 16; + g_debug (" seg_addr:\t0x%08x", seg_addr); break; case DFU_INHX32_RECORD_TYPE_START_SEGMENT: /* initial content of the CS:IP registers */ - addr32 = dfu_utils_buffer_parse_uint32 (in_buffer + offset + 9); + seg_addr = dfu_utils_buffer_parse_uint32 (line + 9); + g_debug (" seg_addr:\t0x%02x", seg_addr); break; case DFU_INHX32_RECORD_TYPE_SIGNATURE: - for (guint i = offset + 9; i < end; i += 2) { - guint8 tmp_c = dfu_utils_buffer_parse_uint8 (in_buffer + i); - g_string_append_c (signature, tmp_c); + for (guint i = 9; i < line_end; i += 2) { + guint8 tmp_c = dfu_utils_buffer_parse_uint8 (line + i); + g_string_append_c (buf_signature, tmp_c); } break; default: @@ -242,17 +282,9 @@ dfu_firmware_from_ihex (DfuFirmware *firmware, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "invalid ihex record type %i", - type); + record_type); return FALSE; } - - /* ignore any line return */ - offset = end + 2; - for (; offset < len_in; offset++) { - if (in_buffer[offset] != '\n' && - in_buffer[offset] != '\r') - break; - } } /* no EOF */ @@ -265,19 +297,19 @@ dfu_firmware_from_ihex (DfuFirmware *firmware, } /* add single image */ - contents = g_bytes_new (string->str, string->len); + contents = g_bytes_new (buf->str, buf->len); dfu_element_set_contents (element, contents); - dfu_element_set_address (element, element_address); + dfu_element_set_address (element, base_addr); dfu_image_add_element (image, element); dfu_firmware_add_image (firmware, image); dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); /* add optional signature */ - if (signature->len > 0) { + if (buf_signature->len > 0) { g_autoptr(DfuElement) element_sig = dfu_element_new (); g_autoptr(DfuImage) image_sig = dfu_image_new (); - g_autoptr(GBytes) data = g_bytes_new_static (signature->str, signature->len); - dfu_element_set_contents (element_sig, data); + g_autoptr(GBytes) data_sig = g_bytes_new_static (buf_signature->str, buf_signature->len); + dfu_element_set_contents (element_sig, data_sig); dfu_image_add_element (image_sig, element_sig); dfu_image_set_name (image_sig, "signature"); dfu_firmware_add_image (firmware, image_sig); @@ -329,7 +361,7 @@ dfu_firmware_to_ihex_bytes (GString *str, guint8 record_type, guint8 buf[2]; fu_common_write_uint16 (buf, address_offset, G_BIG_ENDIAN); dfu_firmware_ihex_emit_chunk (str, 0x0, - DFU_INHX32_RECORD_TYPE_EXTENDED, + DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR, buf, 2); address_offset_last = address_offset; } diff --git a/plugins/dfu/dfu-format-srec.c b/plugins/dfu/dfu-format-srec.c index b99623cac..df239a65b 100644 --- a/plugins/dfu/dfu-format-srec.c +++ b/plugins/dfu/dfu-format-srec.c @@ -38,15 +38,6 @@ dfu_firmware_detect_srec (GBytes *bytes) return DFU_FIRMWARE_FORMAT_SREC; } -typedef enum { - DFU_SREC_RECORD_CLASS_UNKNOWN, - DFU_SREC_RECORD_CLASS_HEADER, - DFU_SREC_RECORD_CLASS_DATA, - DFU_SREC_RECORD_CLASS_TERMINATION, - DFU_SREC_RECORD_CLASS_COUNT, - DFU_SREC_RECORD_CLASS_LAST -} DfuSrecClassType; - /** * dfu_firmware_from_srec: (skip) * @firmware: a #DfuFirmware @@ -65,65 +56,67 @@ dfu_image_from_srec (DfuImage *image, DfuFirmwareParseFlags flags, GError **error) { - const gchar *in_buffer; + const gchar *data; gboolean got_eof = FALSE; gboolean got_hdr = FALSE; - gsize len_in; - guint16 class_data_cnt = 0; + gsize sz = 0; + guint16 data_cnt = 0; guint32 addr32_last = 0; guint32 element_address = 0; - guint offset = 0; - g_autoptr(DfuElement) element = NULL; + g_auto(GStrv) lines = NULL; + g_autoptr(DfuElement) element = dfu_element_new (); g_autoptr(GBytes) contents = NULL; - g_autoptr(GString) modname = g_string_new (NULL); - g_autoptr(GString) outbuf = NULL; + g_autoptr(GString) outbuf = g_string_new (NULL); g_return_val_if_fail (bytes != NULL, FALSE); - /* create element */ - element = dfu_element_new (); - /* parse records */ - in_buffer = g_bytes_get_data (bytes, &len_in); - outbuf = g_string_new (""); - while (offset < len_in) { - DfuSrecClassType rec_class = DFU_SREC_RECORD_CLASS_UNKNOWN; + data = g_bytes_get_data (bytes, &sz); + lines = dfu_utils_strnsplit (data, sz, "\n", -1); + for (guint ln = 0; lines[ln] != NULL; ln++) { + const gchar *line = lines[ln]; + gsize linesz; guint32 rec_addr32; - guint8 rec_count; /* bytes */ - guint8 rec_dataoffset; /* bytes */ + guint8 addrsz = 0; /* bytes */ + guint8 rec_count; /* words */ guint8 rec_kind; + /* ignore blank lines */ + g_strdelimit (lines[ln], "\r", '\0'); + linesz = strlen (line); + if (linesz == 0) + continue; + /* check starting token */ - if (in_buffer[offset] != 'S') { + if (line[0] != 'S') { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid starting token, got 0x%02x at 0x%x", - (guint) in_buffer[offset], offset); + "invalid starting token, got '%c' at line %u", + line[0], ln); return FALSE; } /* check there's enough data for the smallest possible record */ - if (offset + 10 > (guint) len_in) { + if (linesz < 10) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "record incomplete at %u, length %u", - offset, (guint) len_in); + "record incomplete at line %u, length %u", + ln, (guint) linesz); return FALSE; } /* kind, count, address, (data), checksum, linefeed */ - rec_kind = in_buffer[offset + 1]; - rec_count = dfu_utils_buffer_parse_uint8 (in_buffer + offset + 2); - - /* check we can read out this much data */ - if (len_in < offset + (rec_count * 2) + 4) { + rec_kind = line[1] - '0'; + rec_count = dfu_utils_buffer_parse_uint8 (line + 2); + if (rec_count * 2 != linesz - 4) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "file incomplete at %u, length %u", - offset, (guint) len_in); + "count incomplete at line %u, " + "length %u, expected %u", + ln, (guint) linesz - 4, (guint) rec_count * 2); return FALSE; } @@ -132,28 +125,24 @@ dfu_image_from_srec (DfuImage *image, guint8 rec_csum = 0; guint8 rec_csum_expected; for (guint8 i = 0; i < rec_count; i++) - rec_csum += dfu_utils_buffer_parse_uint8 (in_buffer + offset + (i * 2) + 2); + rec_csum += dfu_utils_buffer_parse_uint8 (line + (i * 2) + 2); rec_csum ^= 0xff; - rec_csum_expected = dfu_utils_buffer_parse_uint8 (in_buffer + offset + (rec_count * 2) + 2); + rec_csum_expected = dfu_utils_buffer_parse_uint8 (line + (rec_count * 2) + 2); if (rec_csum != rec_csum_expected) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "checksum incorrect @ 0x%04x, expected %02x, got %02x", - offset, rec_csum_expected, rec_csum); + "checksum incorrect line %u, " + "expected %02x, got %02x", + ln, rec_csum_expected, rec_csum); return FALSE; } } - /* record kind + record count (in bytes, not chars) */ - rec_dataoffset = 2; - - /* parse record */ + /* set each command settings */ switch (rec_kind) { - case '0': - rec_class = DFU_SREC_RECORD_CLASS_HEADER; - rec_dataoffset += 2; - rec_addr32 = dfu_utils_buffer_parse_uint16 (in_buffer + offset + 4); + case 0: + addrsz = 2; if (got_hdr) { g_set_error_literal (error, FWUPD_ERROR, @@ -161,6 +150,63 @@ dfu_image_from_srec (DfuImage *image, "duplicate header record"); return FALSE; } + got_hdr = TRUE; + break; + case 1: + addrsz = 2; + break; + case 2: + addrsz = 3; + break; + case 3: + addrsz = 4; + break; + case 5: + addrsz = 2; + got_eof = TRUE; + break; + case 6: + addrsz = 3; + break; + case 7: + addrsz = 4; + got_eof = TRUE; + break; + case 8: + addrsz = 3; + got_eof = TRUE; + break; + case 9: + addrsz = 2; + got_eof = TRUE; + break; + default: + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid srec record type S%c", + line[1]); + return FALSE; + } + + /* parse address */ + switch (addrsz) { + case 2: + rec_addr32 = dfu_utils_buffer_parse_uint16 (line + 4); + break; + case 3: + rec_addr32 = dfu_utils_buffer_parse_uint24 (line + 4); + break; + case 4: + rec_addr32 = dfu_utils_buffer_parse_uint32 (line + 4); + break; + default: + g_assert_not_reached (); + } + + /* header */ + if (rec_kind == 0) { + g_autoptr(GString) modname = g_string_new (NULL); if (rec_addr32 != 0x0) { g_set_error (error, FWUPD_ERROR, @@ -169,76 +215,34 @@ dfu_image_from_srec (DfuImage *image, rec_addr32); return FALSE; } + /* could be anything, lets assume text */ - for (guint8 i = rec_dataoffset; i <= rec_count; i++) { - guint8 tmp = dfu_utils_buffer_parse_uint8 (in_buffer + offset + (i * 2)); + for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { + guint8 tmp = dfu_utils_buffer_parse_uint8 (line + i); if (!g_ascii_isgraph (tmp)) break; g_string_append_c (modname, tmp); } if (modname->len != 0) dfu_image_set_name (image, modname->str); - got_hdr = TRUE; - break; - case '1': - rec_class = DFU_SREC_RECORD_CLASS_DATA; - rec_dataoffset += 2; - rec_addr32 = dfu_utils_buffer_parse_uint16 (in_buffer + offset + 4); - break; - case '2': - rec_class = DFU_SREC_RECORD_CLASS_DATA; - rec_dataoffset += 3; - rec_addr32 = dfu_utils_buffer_parse_uint24 (in_buffer + offset + 4); - break; - case '3': - rec_class = DFU_SREC_RECORD_CLASS_DATA; - rec_dataoffset += 4; - rec_addr32 = dfu_utils_buffer_parse_uint32 (in_buffer + offset + 4); - break; - case '9': - rec_class = DFU_SREC_RECORD_CLASS_TERMINATION; - rec_addr32 = dfu_utils_buffer_parse_uint16 (in_buffer + offset + 4); - got_eof = TRUE; - break; - case '8': - rec_class = DFU_SREC_RECORD_CLASS_TERMINATION; - rec_addr32 = dfu_utils_buffer_parse_uint24 (in_buffer + offset + 4); - got_eof = TRUE; - break; - case '7': - rec_class = DFU_SREC_RECORD_CLASS_TERMINATION; - rec_addr32 = dfu_utils_buffer_parse_uint32 (in_buffer + offset + 4); - got_eof = TRUE; - break; - case '5': - rec_class = DFU_SREC_RECORD_CLASS_COUNT; - rec_addr32 = dfu_utils_buffer_parse_uint16 (in_buffer + offset + 4); - if (rec_addr32 != class_data_cnt) { + continue; + } + + /* verify we got all records */ + if (rec_kind == 5) { + if (rec_addr32 != data_cnt) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "count record was not valid, got 0x%02x expected 0x%02x", - (guint) rec_addr32, (guint) class_data_cnt); + (guint) rec_addr32, (guint) data_cnt); return FALSE; } - got_eof = TRUE; - break; - default: - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid srec record type S%c", - rec_kind); - return FALSE; } - /* record EOF */ - if (rec_class == DFU_SREC_RECORD_CLASS_TERMINATION) - g_debug ("start execution location: 0x%04x", (guint) rec_addr32); - - /* read data */ - if (rec_class == DFU_SREC_RECORD_CLASS_DATA) { - /* probably invalid data */ + /* data */ + if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) { + /* invalid */ if (!got_hdr) { g_set_error_literal (error, FWUPD_ERROR, @@ -260,23 +264,15 @@ dfu_image_from_srec (DfuImage *image, g_debug ("ignoring data at 0x%x as before start address 0x%x", (guint) rec_addr32, (guint) start_addr); } else { - for (guint8 i = rec_dataoffset; i <= rec_count; i++) { - guint8 tmp = dfu_utils_buffer_parse_uint8 (in_buffer + offset + (i * 2)); + for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { + guint8 tmp = dfu_utils_buffer_parse_uint8 (line + i); g_string_append_c (outbuf, tmp); } if (element_address == 0x0) element_address = rec_addr32; } addr32_last = rec_addr32++; - class_data_cnt++; - } - - /* ignore any line return */ - offset += (rec_count * 2) + 4; - for (; offset < len_in; offset++) { - if (in_buffer[offset] != '\n' && - in_buffer[offset] != '\r') - break; + data_cnt++; } } diff --git a/plugins/dfu/dfu-tool.c b/plugins/dfu/dfu-tool.c index 03672b632..196415f47 100644 --- a/plugins/dfu/dfu-tool.c +++ b/plugins/dfu/dfu-tool.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "dfu-cipher-xtea.h" #include "dfu-device-private.h" @@ -50,6 +49,7 @@ dfu_tool_private_free (DfuToolPrivate *priv) if (priv == NULL) return; g_free (priv->device_vid_pid); + g_object_unref (priv->progressbar); g_object_unref (priv->cancellable); g_object_unref (priv->quirks); if (priv->cmd_array != NULL) @@ -480,7 +480,7 @@ dfu_tool_bytes_replace (GBytes *data, GBytes *search, GBytes *replace) g_return_val_if_fail (search_sz == replace_sz, FALSE); /* find and replace each one */ - for (gsize i = 0; i < data_sz - search_sz; i++) { + for (gsize i = 0; i < data_sz - search_sz + 1; i++) { if (memcmp (data_buf + i, search_buf, search_sz) == 0) { g_print ("Replacing %" G_GSIZE_FORMAT " bytes @0x%04x\n", replace_sz, (guint) i); @@ -675,7 +675,7 @@ dfu_tool_replace_data (DfuToolPrivate *priv, gchar **values, GError **error) if (cnt == 0) { g_set_error_literal (error, FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, + FWUPD_ERROR_NOT_FOUND, "search string was not found"); return FALSE; } @@ -2036,8 +2036,8 @@ dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error) dfu_device_set_usb_context (device, usb_context); if (!fu_device_probe (FU_DEVICE (device), NULL)) continue; - version = as_utils_version_from_uint16 (g_usb_device_get_release (usb_device), - AS_VERSION_PARSE_FLAG_USE_BCD); + version = fu_common_version_from_uint16 (g_usb_device_get_release (usb_device), + FU_VERSION_FORMAT_BCD); g_print ("%s %04x:%04x [v%s]:\n", /* TRANSLATORS: detected a DFU device */ _("Found"), @@ -2427,6 +2427,5 @@ main (int argc, char *argv[]) } /* success/ */ - g_object_unref (priv->progressbar); return EXIT_SUCCESS; } diff --git a/plugins/dfu/dfu.quirk b/plugins/dfu/dfu.quirk index 8c7181751..182fcb830 100644 --- a/plugins/dfu/dfu.quirk +++ b/plugins/dfu/dfu.quirk @@ -1,3 +1,7 @@ +# All DFU devices +[DeviceInstanceId=USB\CLASS_FE&SUBCLASS_01] +Plugin = dfu + # on PC platforms the DW1820A firmware is loaded at runtime and can't # be stored on the device itself as the flash chip is unpopulated [DeviceInstanceId=USB\VID_0A5C&PID_6412] @@ -130,6 +134,7 @@ DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode Plugin = dfu DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode [DeviceInstanceId=USB\VID_03EB&PID_2FF4] +Plugin = dfu DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode # Atmel XMEGA Bootloader diff --git a/plugins/dfu/fu-plugin-dfu.c b/plugins/dfu/fu-plugin-dfu.c index dca1cfc56..4b0682edc 100644 --- a/plugins/dfu/fu-plugin-dfu.c +++ b/plugins/dfu/fu-plugin-dfu.c @@ -6,12 +6,19 @@ #include "config.h" -#include - #include "fu-plugin-vfuncs.h" #include "dfu-device.h" +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.usb.dfu"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.st.dfuse"); +} + static void fu_plugin_dfu_state_changed_cb (DfuDevice *device, DfuState state, diff --git a/plugins/dfu/meson.build b/plugins/dfu/meson.build index 9fdc95bf7..f9d9632dc 100644 --- a/plugins/dfu/meson.build +++ b/plugins/dfu/meson.build @@ -26,12 +26,14 @@ dfu = static_library( 'dfu-target-avr.c', ], dependencies : [ - appstream_glib, giounix, libm, gusb, gudev, ], + link_with : [ + libfwupdprivate, + ], c_args : cargs, include_directories : [ include_directories('../..'), @@ -41,6 +43,7 @@ dfu = static_library( ) shared_module('fu_plugin_dfu', + fu_hash, sources : [ 'fu-plugin-dfu.c', ], @@ -56,12 +59,14 @@ shared_module('fu_plugin_dfu', plugin_deps, ], link_with : [ + libfwupdprivate, dfu, ], ) dfu_tool = executable( 'dfu-tool', + fu_hash, sources : [ 'dfu-tool.c', ], @@ -72,7 +77,7 @@ dfu_tool = executable( include_directories('../../libfwupd'), ], dependencies : [ - appstream_glib, + libxmlb, giounix, libm, gusb, @@ -80,7 +85,6 @@ dfu_tool = executable( ], link_with : [ dfu, - fwupd, libfwupdprivate, ], c_args : cargs, @@ -123,7 +127,7 @@ if get_option('tests') include_directories('../../src'), ], dependencies : [ - appstream_glib, + libxmlb, gio, gusb, gudev, @@ -131,7 +135,6 @@ if get_option('tests') ], link_with : [ dfu, - fwupd, libfwupdprivate, ], c_args : cargs diff --git a/plugins/ebitdo/README.md b/plugins/ebitdo/README.md index 8fe729bcd..ec7a518fb 100644 --- a/plugins/ebitdo/README.md +++ b/plugins/ebitdo/README.md @@ -11,3 +11,23 @@ library and is possible thanks to the vendor open sourcing the flashing tool. The 8Bitdo devices share legacy USB VID/PIDs with other projects and so we have to be a bit careful to not claim other devices as our own. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format. The binary file has a vendor-specific header +that is used when flashing the image. + +This plugin supports the following protocol ID: + + * com.8bitdo + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_2DC8&PID_AB11&REV_0001` + * `USB\VID_2DC8&PID_AB11` + * `USB\VID_2DC8` diff --git a/plugins/ebitdo/ebitdo.quirk b/plugins/ebitdo/ebitdo.quirk index be6bd9e4c..b46c001f8 100644 --- a/plugins/ebitdo/ebitdo.quirk +++ b/plugins/ebitdo/ebitdo.quirk @@ -5,6 +5,7 @@ Flags = is-bootloader [DeviceInstanceId=USB\VID_2DC8&PID_5750] Plugin = ebitdo Flags = is-bootloader +InstallDuration = 120 # FC30 [DeviceInstanceId=USB\VID_1235&PID_AB11] @@ -13,6 +14,7 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_AB11] Plugin = ebitdo Flags = none +InstallDuration = 120 # NES30 [DeviceInstanceId=USB\VID_1235&PID_AB12] @@ -21,6 +23,7 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_AB12] Plugin = ebitdo Flags = none +InstallDuration = 120 # SFC30 [DeviceInstanceId=USB\VID_1235&PID_AB21] @@ -29,6 +32,7 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_AB21] Plugin = ebitdo Flags = none +InstallDuration = 120 # SNES30 [DeviceInstanceId=USB\VID_1235&PID_AB20] @@ -37,6 +41,7 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_AB20] Plugin = ebitdo Flags = none +InstallDuration = 120 # FC30PRO [DeviceInstanceId=USB\VID_1002&PID_9000] @@ -45,6 +50,7 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_9000] Plugin = ebitdo Flags = none +InstallDuration = 120 # NES30PRO [DeviceInstanceId=USB\VID_2002&PID_9000] @@ -53,6 +59,7 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_9001] Plugin = ebitdo Flags = none +InstallDuration = 120 # FC30_ARCADE [DeviceInstanceId=USB\VID_8000&PID_1002] @@ -61,6 +68,7 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_1002] Plugin = ebitdo Flags = none +InstallDuration = 120 # SF30 PRO/SN30 PRO ## Dinput mode (Start + B) @@ -70,7 +78,4 @@ Flags = none [DeviceInstanceId=USB\VID_2DC8&PID_6001] Plugin = ebitdo Flags = none -## Xinput mode (Start + X) -[DeviceInstanceId=USB\VID_045E&PID_028E] -Plugin = ebitdo -Flags = none +InstallDuration = 120 diff --git a/plugins/ebitdo/fu-ebitdo-common.c b/plugins/ebitdo/fu-ebitdo-common.c index 0934f960d..2738931ed 100644 --- a/plugins/ebitdo/fu-ebitdo-common.c +++ b/plugins/ebitdo/fu-ebitdo-common.c @@ -64,20 +64,6 @@ fu_ebitdo_pkt_cmd_to_string (FuEbitdoPktCmd cmd) return NULL; } -void -fu_ebitdo_dump_raw (const gchar *title, const guint8 *data, gsize len) -{ - g_print ("%s:", title); - for (gsize i = strlen (title); i < 16; i++) - g_print (" "); - for (gsize i = 0; i < len; i++) { - g_print ("%02x ", data[i]); - if (i > 0 && i % 32 == 0) - g_print ("\n"); - } - g_print ("\n"); -} - void fu_ebitdo_dump_pkt (FuEbitdoPkt *hdr) { diff --git a/plugins/ebitdo/fu-ebitdo-common.h b/plugins/ebitdo/fu-ebitdo-common.h index db8cf0c5b..84f2e5248 100644 --- a/plugins/ebitdo/fu-ebitdo-common.h +++ b/plugins/ebitdo/fu-ebitdo-common.h @@ -70,8 +70,5 @@ const gchar *fu_ebitdo_pkt_cmd_to_string (FuEbitdoPktCmd cmd); const gchar *fu_ebitdo_pkt_type_to_string (FuEbitdoPktType type); void fu_ebitdo_dump_firmware_header (FuEbitdoFirmwareHeader *hdr); void fu_ebitdo_dump_pkt (FuEbitdoPkt *hdr); -void fu_ebitdo_dump_raw (const gchar *title, - const guint8 *data, - gsize len); #endif /* __FU_EBITDO_COMMON_H */ diff --git a/plugins/ebitdo/fu-ebitdo-device.c b/plugins/ebitdo/fu-ebitdo-device.c index 334f105ae..42cacfa98 100644 --- a/plugins/ebitdo/fu-ebitdo-device.c +++ b/plugins/ebitdo/fu-ebitdo-device.c @@ -66,7 +66,7 @@ fu_ebitdo_device_send (FuEbitdoDevice *self, /* debug */ if (g_getenv ("FWUPD_EBITDO_VERBOSE") != NULL) { - fu_ebitdo_dump_raw ("->DEVICE", packet, (gsize) hdr->pkt_len + 1); + fu_common_dump_raw (G_LOG_DOMAIN, "->DEVICE", packet, (gsize) hdr->pkt_len + 1); fu_ebitdo_dump_pkt (hdr); } @@ -127,7 +127,7 @@ fu_ebitdo_device_receive (FuEbitdoDevice *self, /* debug */ if (g_getenv ("FWUPD_EBITDO_VERBOSE") != NULL) { - fu_ebitdo_dump_raw ("<-DEVICE", packet, actual_length); + fu_common_dump_raw (G_LOG_DOMAIN, "<-DEVICE", packet, actual_length); fu_ebitdo_dump_pkt (hdr); } @@ -215,7 +215,7 @@ static void fu_ebitdo_device_set_version (FuEbitdoDevice *self, guint32 version) { g_autofree gchar *tmp = NULL; - tmp = g_strdup_printf ("%.2f", version / 100.f); + tmp = g_strdup_printf ("%u.%02u", version / 100, version % 100); fu_device_set_version (FU_DEVICE (self), tmp); } diff --git a/plugins/ebitdo/fu-plugin-ebitdo.c b/plugins/ebitdo/fu-plugin-ebitdo.c index fc5a63646..12b6691b1 100644 --- a/plugins/ebitdo/fu-plugin-ebitdo.c +++ b/plugins/ebitdo/fu-plugin-ebitdo.c @@ -13,7 +13,9 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.8bitdo"); } gboolean diff --git a/plugins/ebitdo/meson.build b/plugins/ebitdo/meson.build index a952bae21..99c507581 100644 --- a/plugins/ebitdo/meson.build +++ b/plugins/ebitdo/meson.build @@ -5,6 +5,7 @@ install_data(['ebitdo.quirk'], ) shared_module('fu_plugin_ebitdo', + fu_hash, sources : [ 'fu-plugin-ebitdo.c', 'fu-ebitdo-common.c', @@ -17,6 +18,9 @@ shared_module('fu_plugin_ebitdo', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/fastboot/README.md b/plugins/fastboot/README.md new file mode 100644 index 000000000..43eeb3b9e --- /dev/null +++ b/plugins/fastboot/README.md @@ -0,0 +1,40 @@ +Fastboot Support +================ + +Introduction +------------ + +This plugin is used to update hardware that uses the fastboot protocol. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmare blob in +ZIP file format. Inside the zip file must be all the firmware images for each +partition and a manifest file. The partition images can be in any format, but +the manifest must be either an Android `flashfile.xml` format file, or a QFIL +`partition_nand.xml` format file. + +For both types, all partitions with a defined image found in the zip file will +be updated. + +This plugin supports the following protocol ID: + + * com.google.fastboot + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_18D1&PID_4EE0&REV_0001` + * `USB\VID_18D1&PID_4EE0` + * `USB\VID_18D1` + +Quirk use +--------- +This plugin uses the following plugin-specific quirk: + +| Quirk | Description | Minimum fwupd version | +|------------------------|----------------------------------|-----------------------| +| `FastbootBlockSize` | Block size to use for transfers | 1.2.2 | diff --git a/plugins/fastboot/data/android/flashfile.xml b/plugins/fastboot/data/android/flashfile.xml new file mode 100644 index 000000000..ca70589d7 --- /dev/null +++ b/plugins/fastboot/data/android/flashfile.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/fastboot/data/lsusb.txt b/plugins/fastboot/data/lsusb.txt new file mode 100644 index 000000000..424c956f9 --- /dev/null +++ b/plugins/fastboot/data/lsusb.txt @@ -0,0 +1,58 @@ +Bus 001 Device 025: ID 18d1:4ee0 Google Inc. Nexus 4 (bootloader) +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x18d1 Google Inc. + idProduct 0x4ee0 Nexus 4 (bootloader) + bcdDevice 1.00 + iManufacturer 1 Google + iProduct 2 Android + iSerial 3 034412a082919b5c + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0020 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 66 + bInterfaceProtocol 3 + iInterface 4 fastboot + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/plugins/fastboot/data/qfil/partition_nand.xml b/plugins/fastboot/data/qfil/partition_nand.xml new file mode 100644 index 000000000..503318cce --- /dev/null +++ b/plugins/fastboot/data/qfil/partition_nand.xml @@ -0,0 +1,21 @@ + + + + 0xAA7D1B9A + 0x1F7D48BC + + 0x4 + + + 0:SBL + 0x8 + 0x2 + 0 + 0xFF + 0x01 + 0x00 + 0xFE + sbl1.mbn + + + diff --git a/plugins/fastboot/fastboot.quirk b/plugins/fastboot/fastboot.quirk new file mode 100644 index 000000000..7f16f2e91 --- /dev/null +++ b/plugins/fastboot/fastboot.quirk @@ -0,0 +1,3 @@ +# All fastboot devices +[DeviceInstanceId=USB\CLASS_FF&SUBCLASS_42&PROT_03] +Plugin = fastboot diff --git a/plugins/fastboot/fu-fastboot-device.c b/plugins/fastboot/fu-fastboot-device.c new file mode 100644 index 000000000..5cd541e08 --- /dev/null +++ b/plugins/fastboot/fu-fastboot-device.c @@ -0,0 +1,711 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-archive.h" +#include "fu-chunk.h" +#include "fu-fastboot-device.h" + +#define FASTBOOT_REMOVE_DELAY_RE_ENUMERATE 60000 /* ms */ +#define FASTBOOT_TRANSACTION_TIMEOUT 1000 /* ms */ +#define FASTBOOT_TRANSACTION_RETRY_MAX 600 +#define FASTBOOT_EP_IN 0x81 +#define FASTBOOT_EP_OUT 0x01 +#define FASTBOOT_CMD_BUFSZ 64 /* bytes */ + +struct _FuFastbootDevice { + FuUsbDevice parent_instance; + gboolean secure; + guint blocksz; + guint8 intf_nr; +}; + +G_DEFINE_TYPE (FuFastbootDevice, fu_fastboot_device, FU_TYPE_USB_DEVICE) + +static void +fu_fastboot_device_to_string (FuDevice *device, GString *str) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); + g_string_append (str, " FuFastbootDevice:\n"); + g_string_append_printf (str, " intf:\t0x%02x\n", (guint) self->intf_nr); + g_string_append_printf (str, " secure:\t%i\n", self->secure); + g_string_append_printf (str, " blocksz:\t%u\n", self->blocksz); +} + +static gboolean +fu_fastboot_device_probe (FuDevice *device, GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + g_autoptr(GUsbInterface) intf = NULL; + + /* find the correct fastboot interface */ + intf = g_usb_device_get_interface (usb_device, 0xff, 0x42, 0x03, error); + if (intf == NULL) + return FALSE; + self->intf_nr = g_usb_interface_get_number (intf); + return TRUE; +} + +static gboolean +fu_fastboot_device_open (FuUsbDevice *device, GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + if (!g_usb_device_claim_interface (usb_device, self->intf_nr, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to claim interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_fastboot_buffer_dump (const gchar *title, const guint8 *buf, gsize sz) +{ + if (g_getenv ("FWUPD_FASTBOOT_VERBOSE") == NULL) + return; + g_print ("%s (%" G_GSIZE_FORMAT "):\n", title, sz); + for (gsize i = 0; i < sz; i++) { + g_print ("%02x[%c] ", buf[i], g_ascii_isprint (buf[i]) ? buf[i] : '?'); + if (i > 0 && (i + 1) % 256 == 0) + g_print ("\n"); + } + g_print ("\n"); +} + +static gboolean +fu_fastboot_device_write (FuDevice *device, const guint8 *buf, gsize buflen, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); + gboolean ret; + gsize actual_len = 0; + g_autofree guint8 *buf2 = g_memdup (buf, (guint) buflen); + + fu_fastboot_buffer_dump ("writing", buf, buflen); + ret = g_usb_device_bulk_transfer (usb_device, + FASTBOOT_EP_OUT, + buf2, + buflen, + &actual_len, + FASTBOOT_TRANSACTION_TIMEOUT, + NULL, error); + if (!ret) { + g_prefix_error (error, "failed to do bulk transfer: "); + return FALSE; + } + if (actual_len != buflen) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only wrote %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_fastboot_device_writestr (FuDevice *device, const gchar *str, GError **error) +{ + gsize buflen = strlen (str); + if (buflen > FASTBOOT_CMD_BUFSZ - 4) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "fastboot limits writes to %i bytes", + FASTBOOT_CMD_BUFSZ - 4); + return FALSE; + } + return fu_fastboot_device_write (device, (const guint8 *) str, buflen, error); +} + +typedef enum { + FU_FASTBOOT_DEVICE_READ_FLAG_NONE, + FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, +} FuFastbootDeviceReadFlags; + +static gboolean +fu_fastboot_device_read (FuDevice *device, + gchar **str, + FuFastbootDeviceReadFlags flags, + GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); + guint retries = 1; + + /* these commands may return INFO or take some time to complete */ + if (flags & FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL) + retries = FASTBOOT_TRANSACTION_RETRY_MAX; + + for (guint i = 0; i < retries; i++) { + gboolean ret; + gsize actual_len = 0; + guint8 buf[FASTBOOT_CMD_BUFSZ] = { 0x00 }; + g_autofree gchar *tmp = NULL; + g_autoptr(GError) error_local = NULL; + + ret = g_usb_device_bulk_transfer (usb_device, + FASTBOOT_EP_IN, + buf, + sizeof(buf), + &actual_len, + FASTBOOT_TRANSACTION_TIMEOUT, + NULL, &error_local); + if (!ret) { + if (g_error_matches (error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_TIMED_OUT)) { + g_debug ("ignoring %s", error_local->message); + continue; + } + g_propagate_prefixed_error (error, + g_steal_pointer (&error_local), + "failed to do bulk transfer: "); + return FALSE; + } + fu_fastboot_buffer_dump ("read", buf, actual_len); + if (actual_len < 4) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only read %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + + /* info */ + tmp = g_strndup ((const gchar *) buf + 4, self->blocksz - 4); + if (memcmp (buf, "INFO", 4) == 0) { + if (g_strcmp0 (tmp, "erasing flash") == 0) + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + else if (g_strcmp0 (tmp, "writing flash") == 0) + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + else + g_debug ("INFO returned unknown: %s", tmp); + continue; + } + + /* success */ + if (memcmp (buf, "OKAY", 4) == 0 || memcmp (buf, "DATA", 4) == 0) { + if (str != NULL) + *str = g_steal_pointer (&tmp); + return TRUE; + } + + /* failure */ + if (memcmp (buf, "FAIL", 4) == 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to read response: %s", tmp); + return FALSE; + } + + /* unknown failure */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to read response"); + return FALSE; + } + + /* we timed out a *lot* */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "no response to read"); + return FALSE; +} + +static gboolean +fu_fastboot_device_getvar (FuDevice *device, const gchar *key, gchar **str, GError **error) +{ + g_autofree gchar *tmp = g_strdup_printf ("getvar:%s", key); + if (!fu_fastboot_device_writestr (device, tmp, error)) + return FALSE; + if (!fu_fastboot_device_read (device, str, + FU_FASTBOOT_DEVICE_READ_FLAG_NONE, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_fastboot_device_cmd (FuDevice *device, const gchar *cmd, + FuFastbootDeviceReadFlags flags, GError **error) +{ + if (!fu_fastboot_device_writestr (device, cmd, error)) + return FALSE; + if (!fu_fastboot_device_read (device, NULL, flags, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_fastboot_device_flash (FuDevice *device, const gchar *partition, GError **error) +{ + g_autofree gchar *tmp = g_strdup_printf ("flash:%s", partition); + return fu_fastboot_device_cmd (device, tmp, + FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, + error); +} + +static gboolean +fu_fastboot_device_download (FuDevice *device, GBytes *fw, 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(GPtrArray) chunks = NULL; + + /* tell the client the size of data to expect */ + if (!fu_fastboot_device_cmd (device, tmp, + FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, + error)) + return FALSE; + + /* send the data in chunks */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + chunks = fu_chunk_array_new_from_bytes (fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + self->blocksz); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (!fu_fastboot_device_write (device, chk->data, chk->data_sz, error)) + return FALSE; + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len * 2); + } + if (!fu_fastboot_device_read (device, NULL, + FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_fastboot_device_setup (FuDevice *device, GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); + g_autofree gchar *product = NULL; + g_autofree gchar *serialno = NULL; + g_autofree gchar *version = NULL; + g_autofree gchar *secure = NULL; + g_autofree gchar *version_bootloader = NULL; + + /* product */ + if (!fu_fastboot_device_getvar (device, "product", &product, error)) + return FALSE; + if (product != NULL && product[0] != '\0') { + g_autofree gchar *tmp = g_strdup_printf ("Fastboot %s", product); + fu_device_set_name (device, tmp); + } + + /* fastboot API version */ + if (!fu_fastboot_device_getvar (device, "version", &version, error)) + return FALSE; + if (version != NULL && version[0] != '\0') + g_debug ("fastboot version=%s", version); + + /* bootloader version */ + if (!fu_fastboot_device_getvar (device, "version-bootloader", &version_bootloader, error)) + return FALSE; + 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)) + return FALSE; + if (serialno != NULL && serialno[0] != '\0') + fu_device_set_serial (device, serialno); + + /* secure */ + if (!fu_fastboot_device_getvar (device, "secure", &secure, error)) + return FALSE; + if (secure != NULL && secure[0] != '\0') + self->secure = TRUE; + + /* success */ + return TRUE; +} + +static gboolean +fu_fastboot_device_write_qfil_part (FuDevice *device, + FuArchive *archive, + XbNode *part, + GError **error) +{ + GBytes *data; + const gchar *fn; + const gchar *partition; + + /* not all partitions have images */ + fn = xb_node_query_text (part, "img_name", NULL); + if (fn == NULL) + return TRUE; + + /* find filename */ + data = fu_archive_lookup_by_fn (archive, fn, error); + if (data == NULL) + return FALSE; + + /* get the partition name */ + partition = xb_node_query_text (part, "name", error); + if (partition == NULL) + return FALSE; + if (g_str_has_prefix (partition, "0:")) + partition += 2; + + /* flash the partition */ + if (!fu_fastboot_device_download (device, data, error)) + return FALSE; + return fu_fastboot_device_flash (device, partition, error); +} + +static gboolean +fu_fastboot_device_write_motorola_part (FuDevice *device, + FuArchive *archive, + XbNode *part, + GError **error) +{ + const gchar *op = xb_node_get_attr (part, "operation"); + + /* oem */ + if (g_strcmp0 (op, "oem") == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "OEM commands are not supported"); + return FALSE; + } + + /* getvar */ + if (g_strcmp0 (op, "getvar") == 0) { + const gchar *var = xb_node_get_attr (part, "var"); + g_autofree gchar *tmp = NULL; + + /* check required args */ + if (var == NULL) { + tmp = xb_node_export (part, XB_NODE_EXPORT_FLAG_NONE, NULL); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "required var for part: %s", tmp); + return FALSE; + } + + /* just has to be non-empty */ + if (!fu_fastboot_device_getvar (device, var, &tmp, error)) + return FALSE; + if (tmp == NULL || tmp[0] == '\0') { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to getvar %s", var); + return FALSE; + } + return TRUE; + } + + /* erase */ + if (g_strcmp0 (op, "erase") == 0) { + const gchar *partition = xb_node_get_attr (part, "partition"); + g_autofree gchar *cmd = g_strdup_printf ("erase:%s", partition); + + /* check required args */ + if (partition == NULL) { + g_autofree gchar *tmp = NULL; + tmp = xb_node_export (part, XB_NODE_EXPORT_FLAG_NONE, NULL); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "required partition for part: %s", tmp); + return FALSE; + } + + /* erase the partition */ + return fu_fastboot_device_cmd (device, cmd, + FU_FASTBOOT_DEVICE_READ_FLAG_NONE, + error); + } + + /* flash */ + if (g_strcmp0 (op, "flash") == 0) { + GBytes *data; + const gchar *filename = xb_node_get_attr (part, "filename"); + const gchar *partition = xb_node_get_attr (part, "partition"); + struct { + GChecksumType kind; + const gchar *str; + } csum_kinds[] = { + { G_CHECKSUM_MD5, "MD5" }, + { G_CHECKSUM_SHA1, "SHA1" }, + { 0, NULL } + }; + + /* check required args */ + if (partition == NULL || filename == NULL) { + g_autofree gchar *tmp = NULL; + tmp = xb_node_export (part, XB_NODE_EXPORT_FLAG_NONE, NULL); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "required partition and filename: %s", tmp); + return FALSE; + } + + /* find filename */ + data = fu_archive_lookup_by_fn (archive, filename, error); + if (data == NULL) + return FALSE; + + /* checksum is optional */ + for (guint i = 0; csum_kinds[i].str != NULL; i++) { + const gchar *csum; + g_autofree gchar *csum_actual = NULL; + + /* not provided */ + csum = xb_node_get_attr (part, csum_kinds[i].str); + if (csum == NULL) + continue; + + /* check is valid */ + csum_actual = g_compute_checksum_for_bytes (csum_kinds[i].kind, data); + if (g_strcmp0 (csum, csum_actual) != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%s invalid, expected %s, got %s", + filename, csum, csum_actual); + return FALSE; + } + } + + /* flash the partition */ + if (!fu_fastboot_device_download (device, data, error)) + return FALSE; + return fu_fastboot_device_flash (device, partition, 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, op, + FU_FASTBOOT_DEVICE_READ_FLAG_NONE, + error); + } + + /* unknown */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unknown operation %s", op); + return FALSE; +} + +static gboolean +fu_fastboot_device_write_motorola (FuDevice *device, + FuArchive* archive, + GError **error) +{ + GBytes *data; + g_autoptr(GPtrArray) parts = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autoptr(XbSilo) silo = NULL; + + /* load the manifest of operations */ + data = fu_archive_lookup_by_fn (archive, "flashfile.xml", error); + if (data == NULL) + return FALSE; + if (!xb_builder_source_load_bytes (source, data, + 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; + + /* get all the operation parts */ + parts = xb_silo_query (silo, "parts/part", 0, error); + if (parts == NULL) + return FALSE; + for (guint i = 0; i < parts->len; i++) { + XbNode *part = g_ptr_array_index (parts, i); + if (!fu_fastboot_device_write_motorola_part (device, + archive, + part, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fastboot_device_write_qfil (FuDevice *device, FuArchive* archive, GError **error) +{ + GBytes *data; + g_autoptr(GPtrArray) parts = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autoptr(XbSilo) silo = NULL; + + /* load the manifest of operations */ + data = fu_archive_lookup_by_fn (archive, "partition_nand.xml", error); + if (data == NULL) + return FALSE; + if (!xb_builder_source_load_bytes (source, data, + 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; + + /* get all the operation parts */ + parts = xb_silo_query (silo, "nandboot/partitions/partition", 0, error); + if (parts == NULL) + return FALSE; + for (guint i = 0; i < parts->len; i++) { + XbNode *part = g_ptr_array_index (parts, i); + if (!fu_fastboot_device_write_qfil_part (device, + archive, + part, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fastboot_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +{ + g_autoptr(FuArchive) archive = NULL; + + /* decompress entire archive ahead of time */ + archive = fu_archive_new (fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + + /* load the manifest of operations */ + if (fu_archive_lookup_by_fn (archive, "partition_nand.xml", NULL) != NULL) + return fu_fastboot_device_write_qfil (device, archive, error); + if (fu_archive_lookup_by_fn (archive, "flashfile.xml", NULL) != NULL) { + return fu_fastboot_device_write_motorola (device, + archive, + error); + } + + /* not supported */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "manifest not supported"); + return FALSE; +} + +static gboolean +fu_fastboot_device_close (FuUsbDevice *device, GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* we're done here */ + if (!g_usb_device_release_interface (usb_device, self->intf_nr, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to release interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fastboot_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); + + /* load slave address from quirks */ + if (g_strcmp0 (key, "FastbootBlockSize") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp >= 0x40 && tmp < 0x100000) { + self->blocksz = tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid block size"); + return FALSE; + } + + /* failed */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; + +} + +static gboolean +fu_fastboot_device_attach (FuDevice *device, GError **error) +{ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + return fu_fastboot_device_cmd (device, "reboot", + FU_FASTBOOT_DEVICE_READ_FLAG_NONE, + error); +} + +static void +fu_fastboot_device_init (FuFastbootDevice *self) +{ + /* this is a safe default, even using USBv1 */ + self->blocksz = 512; + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_remove_delay (FU_DEVICE (self), FASTBOOT_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_fastboot_device_class_init (FuFastbootDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->probe = fu_fastboot_device_probe; + klass_device->setup = fu_fastboot_device_setup; + klass_device->write_firmware = fu_fastboot_device_write_firmware; + klass_device->attach = fu_fastboot_device_attach; + klass_device->to_string = fu_fastboot_device_to_string; + klass_device->set_quirk_kv = fu_fastboot_device_set_quirk_kv; + klass_usb_device->open = fu_fastboot_device_open; + klass_usb_device->close = fu_fastboot_device_close; +} + +FuFastbootDevice * +fu_fastboot_device_new (FuUsbDevice *device) +{ + FuFastbootDevice *self = g_object_new (FU_TYPE_FASTBOOT_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/fastboot/fu-fastboot-device.h b/plugins/fastboot/fu-fastboot-device.h new file mode 100644 index 000000000..5f44c9167 --- /dev/null +++ b/plugins/fastboot/fu-fastboot-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_FASTBOOT_DEVICE_H +#define __FU_FASTBOOT_DEVICE_H + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_FASTBOOT_DEVICE (fu_fastboot_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuFastbootDevice, fu_fastboot_device, FU, FASTBOOT_DEVICE, FuUsbDevice) + +FuFastbootDevice *fu_fastboot_device_new (FuUsbDevice *device); + +G_END_DECLS + +#endif /* __FU_FASTBOOT_DEVICE_H */ diff --git a/plugins/fastboot/fu-plugin-fastboot.c b/plugins/fastboot/fu-plugin-fastboot.c new file mode 100644 index 000000000..0d372fc08 --- /dev/null +++ b/plugins/fastboot/fu-plugin-fastboot.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-fastboot-device.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.google.fastboot"); +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, error); +} + +gboolean +fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* reset */ + if (!fu_device_attach (device, error)) + return FALSE; + + /* wait for replug */ + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuFastbootDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + dev = fu_fastboot_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} diff --git a/plugins/fastboot/meson.build b/plugins/fastboot/meson.build new file mode 100644 index 000000000..09d9b2323 --- /dev/null +++ b/plugins/fastboot/meson.build @@ -0,0 +1,27 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginFastboot"'] + +install_data(['fastboot.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_fastboot', + fu_hash, + sources : [ + 'fu-plugin-fastboot.c', + 'fu-fastboot-device.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/flashrom/README.md b/plugins/flashrom/README.md index 4df977c38..bc19ca43f 100644 --- a/plugins/flashrom/README.md +++ b/plugins/flashrom/README.md @@ -5,3 +5,21 @@ Introduction ------------ This plugin uses `flashrom` to update the system firmware. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format, which is typically the raw input for an +EEPROM programmer. + +This plugin supports the following protocol ID: + + * org.flashrom + +GUID Generation +--------------- + +These device uses hardware ID values which are derived from SMBIOS. They should +match the values provided by `fwupdtool hwids` or the `ComputerHardwareIds.exe` +Windows utility. diff --git a/plugins/flashrom/fu-plugin-flashrom.c b/plugins/flashrom/fu-plugin-flashrom.c index 34ddfe2b6..9085ffe50 100644 --- a/plugins/flashrom/fu-plugin-flashrom.c +++ b/plugins/flashrom/fu-plugin-flashrom.c @@ -32,7 +32,9 @@ struct FuPluginData { void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.flashrom"); } void @@ -66,6 +68,7 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) g_autofree gchar *device_id = g_strdup_printf ("flashrom-%s", quirk_str); g_autoptr(FuDevice) dev = fu_device_new (); fu_device_set_id (dev, device_id); + fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin)); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); if (data->flashrom_fn != NULL) { fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); diff --git a/plugins/flashrom/meson.build b/plugins/flashrom/meson.build index 1b4250f03..222b5f88f 100644 --- a/plugins/flashrom/meson.build +++ b/plugins/flashrom/meson.build @@ -5,6 +5,7 @@ install_data(['flashrom.quirk'], ) shared_module('fu_plugin_flashrom', + fu_hash, sources : [ 'fu-plugin-flashrom.c', ], @@ -15,6 +16,9 @@ shared_module('fu_plugin_flashrom', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : [ cargs, '-DLOCALSTATEDIR="' + localstatedir + '"', diff --git a/plugins/meson.build b/plugins/meson.build index e62f82c5e..1da7c5a5a 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,6 +1,8 @@ +subdir('ata') subdir('dfu') subdir('colorhug') subdir('ebitdo') +subdir('fastboot') subdir('flashrom') subdir('steelseries') subdir('dell-dock') @@ -11,7 +13,8 @@ subdir('test') subdir('udev') subdir('unifying') subdir('upower') -subdir('wacomhid') +subdir('wacom-raw') +subdir('wacom-usb') subdir('superio') # depends on dfu diff --git a/plugins/nitrokey/README.md b/plugins/nitrokey/README.md index fa01fd052..df0151ea9 100644 --- a/plugins/nitrokey/README.md +++ b/plugins/nitrokey/README.md @@ -10,3 +10,12 @@ available from the vendor. The device is switched to a DFU bootloader only when the secret firmware pin is entered into the nitrokey-app tool. This cannot be automated. + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_20A0&PID_4109&REV_0001` + * `USB\VID_20A0&PID_4109` + * `USB\VID_20A0` diff --git a/plugins/nitrokey/fu-nitrokey-common.h b/plugins/nitrokey/fu-nitrokey-common.h index 44b944ae4..894e1ba83 100644 --- a/plugins/nitrokey/fu-nitrokey-common.h +++ b/plugins/nitrokey/fu-nitrokey-common.h @@ -14,6 +14,56 @@ G_BEGIN_DECLS guint32 fu_nitrokey_perform_crc32 (const guint8 *data, gsize size); +#define NITROKEY_TRANSACTION_TIMEOUT 100 /* ms */ +#define NITROKEY_NR_RETRIES 5 + +#define NITROKEY_REQUEST_DATA_LENGTH 59 +#define NITROKEY_REPLY_DATA_LENGTH 53 + +#define NITROKEY_CMD_GET_DEVICE_STATUS (0x20 + 14) + +typedef struct __attribute__((packed)) { + guint8 command; + guint8 payload[NITROKEY_REQUEST_DATA_LENGTH]; + guint32 crc; +} NitrokeyHidRequest; + +typedef struct __attribute__((packed)) { + guint8 device_status; + guint8 command_id; + guint32 last_command_crc; + guint8 last_command_status; + guint8 payload[NITROKEY_REPLY_DATA_LENGTH]; + guint32 crc; +} NitrokeyHidResponse; + +/* based from libnitrokey/stick20_commands.h from libnitrokey v3.4.1 */ +typedef struct __attribute__((packed)) { + guint8 _padding[18]; /* stick20_commands.h:132 // 26 - 8 = 18 */ + guint8 SendCounter; + guint8 SendDataType; + guint8 FollowBytesFlag; + guint8 SendSize; + guint16 MagicNumber_StickConfig; + guint8 ReadWriteFlagUncryptedVolume; + guint8 ReadWriteFlagCryptedVolume; + guint8 VersionMajor; + guint8 VersionMinor; + guint8 VersionReservedByte; + guint8 VersionBuildIteration; + guint8 ReadWriteFlagHiddenVolume; + guint8 FirmwareLocked; + guint8 NewSDCardFound; + guint8 SDFillWithRandomChars; + guint32 ActiveSD_CardID; + guint8 VolumeActiceFlag; + guint8 NewSmartCardFound; + guint8 UserPwRetryCount; + guint8 AdminPwRetryCount; + guint32 ActiveSmartCardID; + guint8 StickKeysNotInitiated; +} NitrokeyGetDeviceStatusPayload; + G_END_DECLS #endif /* __FU_NITROKEY_COMMON_H */ diff --git a/plugins/nitrokey/fu-nitrokey-device.c b/plugins/nitrokey/fu-nitrokey-device.c index bc2848975..16860fa4b 100644 --- a/plugins/nitrokey/fu-nitrokey-device.c +++ b/plugins/nitrokey/fu-nitrokey-device.c @@ -13,56 +13,6 @@ G_DEFINE_TYPE (FuNitrokeyDevice, fu_nitrokey_device, FU_TYPE_USB_DEVICE) -#define NITROKEY_TRANSACTION_TIMEOUT 100 /* ms */ -#define NITROKEY_NR_RETRIES 5 - -#define NITROKEY_REQUEST_DATA_LENGTH 59 -#define NITROKEY_REPLY_DATA_LENGTH 53 - -#define NITROKEY_CMD_GET_DEVICE_STATUS (0x20 + 14) - -typedef struct __attribute__((packed)) { - guint8 command; - guint8 payload[NITROKEY_REQUEST_DATA_LENGTH]; - guint32 crc; -} NitrokeyHidRequest; - -typedef struct __attribute__((packed)) { - guint8 _padding; /* always zero */ - guint8 device_status; - guint32 last_command_crc; - guint8 last_command_status; - guint8 payload[NITROKEY_REPLY_DATA_LENGTH]; - guint32 crc; -} NitrokeyHidResponse; - -/* based from libnitrokey/stick20_commands.h */ -typedef struct __attribute__((packed)) { - guint8 _padding[24]; - guint8 SendCounter; - guint8 SendDataType; - guint8 FollowBytesFlag; - guint8 SendSize; - guint16 MagicNumber_StickConfig; - guint8 ReadWriteFlagUncryptedVolume; - guint8 ReadWriteFlagCryptedVolume; - guint8 VersionReserved1; - guint8 VersionMinor; - guint8 VersionReserved2; - guint8 VersionMajor; - guint8 ReadWriteFlagHiddenVolume; - guint8 FirmwareLocked; - guint8 NewSDCardFound; - guint8 SDFillWithRandomChars; - guint32 ActiveSD_CardID; - guint8 VolumeActiceFlag; - guint8 NewSmartCardFound; - guint8 UserPwRetryCount; - guint8 AdminPwRetryCount; - guint32 ActiveSmartCardID; - guint8 StickKeysNotInitiated; -} NitrokeyGetDeviceStatusPayload; - static void _dump_to_console (const gchar *title, const guint8 *buf, gsize buf_sz) { @@ -240,8 +190,8 @@ fu_nitrokey_device_setup (FuDevice *device, GError **error) return FALSE; } _dump_to_console ("payload", buf_reply, sizeof(buf_reply)); - memcpy (&payload, buf_reply, sizeof(buf_reply)); - version = g_strdup_printf ("%u.%u", payload.VersionMinor, payload.VersionMajor); + memcpy (&payload, buf_reply, sizeof(payload)); + version = g_strdup_printf ("%u.%u", payload.VersionMajor, payload.VersionMinor); fu_device_set_version (FU_DEVICE (device), version); /* success */ diff --git a/plugins/nitrokey/fu-plugin-nitrokey.c b/plugins/nitrokey/fu-plugin-nitrokey.c index 9c4e23609..4061ee7d7 100644 --- a/plugins/nitrokey/fu-plugin-nitrokey.c +++ b/plugins/nitrokey/fu-plugin-nitrokey.c @@ -14,6 +14,7 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); } diff --git a/plugins/nitrokey/fu-self-test.c b/plugins/nitrokey/fu-self-test.c index acf0a3db0..dc28579f3 100644 --- a/plugins/nitrokey/fu-self-test.c +++ b/plugins/nitrokey/fu-self-test.c @@ -10,6 +10,59 @@ #include "fu-nitrokey-common.h" +static void +fu_nitrokey_version_test (void) +{ + /* use the Nitrokey Storage v0.53 status response for test, CRC 0xa2762d14 */ + NitrokeyGetDeviceStatusPayload payload; + NitrokeyHidResponse res; + guint32 crc_tmp; + /* 65 bytes of response from HIDAPI; first byte is always 0 */ + const guint8 buf[] = { + /*0x00,*/ + 0x00, 0x2e, 0xef, 0xc4, 0x9b, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x2e, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x18, 0x33, + 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x45, 0x24, 0xf1, 0x4c, 0x01, 0x00, + 0x03, 0x03, 0xc7, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x2d, 0x76, + 0xa2 }; + + /* testing the whole path, as in fu_nitrokey_device_setup()*/ + memcpy (&res, buf, sizeof (buf)); + memcpy (&payload, &res.payload, sizeof (payload)); + + /* verify the version number */ + g_assert_cmpint (payload.VersionMajor, == , 0); + g_assert_cmpint (payload.VersionMinor, == , 53); + g_assert_cmpint (buf[34], == , payload.VersionMinor); + g_assert_cmpint (payload.VersionBuildIteration, == , 0); + + /* verify the response checksum */ + crc_tmp = fu_nitrokey_perform_crc32 (buf, sizeof (res) - 4); + g_assert_cmpint (GUINT32_FROM_LE (res.crc), == , crc_tmp); + +} + +static void +fu_nitrokey_version_test_static (void) +{ + /* use static response from numbered bytes, to make sure fields occupy + * expected bytes */ + NitrokeyGetDeviceStatusPayload payload; + NitrokeyHidResponse res; + + const guint8 buf[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + }; + memcpy (&res, buf, sizeof (buf)); + memcpy (&payload, &res.payload, sizeof (payload)); + g_assert_cmpint (payload.VersionMajor, == , 33); /* 0x1a */ + g_assert_cmpint (payload.VersionMinor, == , 34); /* 0x1b */ + g_assert_cmpint (buf[34], == , 34); +} + static void fu_nitrokey_func (void) { @@ -31,5 +84,7 @@ main (int argc, char **argv) /* tests go here */ g_test_add_func ("/fwupd/nitrokey", fu_nitrokey_func); + g_test_add_func ("/fwupd/nitrokey-version-static", fu_nitrokey_version_test_static); + g_test_add_func ("/fwupd/nitrokey-version", fu_nitrokey_version_test); return g_test_run (); } diff --git a/plugins/nitrokey/meson.build b/plugins/nitrokey/meson.build index 324958fc6..469114985 100644 --- a/plugins/nitrokey/meson.build +++ b/plugins/nitrokey/meson.build @@ -5,6 +5,7 @@ install_data(['nitrokey.quirk'], ) shared_module('fu_plugin_nitrokey', + fu_hash, sources : [ 'fu-nitrokey-device.c', 'fu-nitrokey-common.c', @@ -17,6 +18,9 @@ shared_module('fu_plugin_nitrokey', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/nvme/README.md b/plugins/nvme/README.md index dd189dea0..0be4da61a 100644 --- a/plugins/nvme/README.md +++ b/plugins/nvme/README.md @@ -10,3 +10,42 @@ firmware file. Firmware is sent in 4kB chunks and activated on next reboot. The device GUID is read from the vendor specific area and if not found then generated from the trimmed model string. + +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: + + * org.nvmexpress + +GUID Generation +--------------- + +These device use the NVMe DeviceInstanceId values, e.g. + + * `NVME\VEN_1179&DEV_010F&REV_01` + * `NVME\VEN_1179&DEV_010F` + * `NVME\VEN_1179` + +The FRU globally unique identifier (FGUID) is also added from the CNS if set. +Please refer to this document for more details on how to add support for FGUID: +https://nvmexpress.org/wp-content/uploads/NVM_Express_Revision_1.3.pdf + +Additionally, for NVMe drives with Dell vendor firmware two extra GUIDs are +added: + + * `STORAGE-DELL-${component-id}` + +and any optional GUID saved in the vendor extension block. + +Quirk use +--------- +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|------------------------|---------------------------------------------|-----------------------| +| `NvmeBlockSize` | The block size used for NVMe writes | 1.1.3 | +| `Flags` | `force-align` if image should be padded | 1.2.4 | diff --git a/plugins/nvme/fu-nvme-common.c b/plugins/nvme/fu-nvme-common.c new file mode 100644 index 000000000..830fab0f8 --- /dev/null +++ b/plugins/nvme/fu-nvme-common.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#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_ACIVATE_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 --git a/plugins/nvme/fu-nvme-common.h b/plugins/nvme/fu-nvme-common.h new file mode 100644 index 000000000..0cdac7782 --- /dev/null +++ b/plugins/nvme/fu-nvme-common.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_NVME_COMMON_H +#define __FU_NVME_COMMON_H + +#include + +G_BEGIN_DECLS + +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_ACIVATE_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); + +G_END_DECLS + +#endif /* __FU_NVME_COMMON_H */ diff --git a/plugins/nvme/fu-nvme-device.c b/plugins/nvme/fu-nvme-device.c index 060f7a423..d39609b14 100644 --- a/plugins/nvme/fu-nvme-device.c +++ b/plugins/nvme/fu-nvme-device.c @@ -19,13 +19,13 @@ #include #include "fu-chunk.h" +#include "fu-nvme-common.h" #include "fu-nvme-device.h" #define FU_NVME_ID_CTRL_SIZE 0x1000 struct _FuNvmeDevice { FuUdevDevice parent_instance; - gchar *version_format; guint pci_depth; gint fd; guint64 write_block_size; @@ -101,7 +101,12 @@ fu_nvme_device_get_guid_safe (const guint8 *buf, guint16 addr_start) static gboolean fu_nvme_device_submit_admin_passthru (FuNvmeDevice *self, struct nvme_admin_cmd *cmd, GError **error) { - if (ioctl (self->fd, NVME_IOCTL_ADMIN_CMD, cmd) < 0) { + gint rc; + guint32 err; + + /* submit admin command */ + rc = ioctl (self->fd, NVME_IOCTL_ADMIN_CMD, cmd); + if (rc < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -110,7 +115,26 @@ fu_nvme_device_submit_admin_passthru (FuNvmeDevice *self, struct nvme_admin_cmd strerror (errno)); return FALSE; } - return TRUE; + + /* check the error code */ + err = rc & 0x3ff; + switch (err) { + case NVME_SC_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: + return TRUE; + default: + break; + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported: %s", + fu_nvme_status_to_string (err)); + return FALSE; + } static gboolean @@ -153,8 +177,8 @@ fu_nvme_device_fw_download (FuNvmeDevice *self, .opcode = 0x11, .addr = 0x0, /* memory address of data */ .data_len = data_sz, - .cdw10 = (data_sz >> 2) - 1, - .cdw11 = addr >> 2, + .cdw10 = (data_sz >> 2) - 1, /* convert to DWORDs */ + .cdw11 = addr >> 2, /* convert to DWORDs */ }; memcpy (&cmd.addr, &data, sizeof (gpointer)); return fu_nvme_device_submit_admin_passthru (self, &cmd, error); @@ -186,13 +210,13 @@ static gboolean fu_nvme_device_set_version (FuNvmeDevice *self, const gchar *version, GError **error) { /* unset */ - if (self->version_format == NULL) { + if (fu_device_get_version_format (FU_DEVICE (self)) == FU_VERSION_FORMAT_UNKNOWN) { fu_device_set_version (FU_DEVICE (self), version); return TRUE; } /* AA.BB.CC.DD */ - if (g_strcmp0 (self->version_format, "quad") == 0) { + if (fu_device_get_version_format (FU_DEVICE (self)) == FU_VERSION_FORMAT_QUAD) { guint64 tmp = g_ascii_strtoull (version, NULL, 16); g_autofree gchar *version_new = NULL; if (tmp == 0 || tmp > G_MAXUINT32) { @@ -203,17 +227,16 @@ fu_nvme_device_set_version (FuNvmeDevice *self, const gchar *version, GError **e version); return FALSE; } - version_new = as_utils_version_from_uint32 (tmp, AS_VERSION_PARSE_FLAG_NONE); + version_new = fu_common_version_from_uint32 (tmp, FU_VERSION_FORMAT_QUAD); fu_device_set_version (FU_DEVICE (self), version_new); return TRUE; } /* invalid, or not supported */ - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "version format %s not recognised", - self->version_format); + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "version format not recognised"); return FALSE; } @@ -221,8 +244,10 @@ static gboolean fu_nvme_device_parse_cns (FuNvmeDevice *self, const guint8 *buf, gsize sz, GError **error) { guint8 fawr; + guint8 fwug; guint8 nfws; guint8 s1ro; + g_autofree gchar *gu = NULL; g_autofree gchar *mn = NULL; g_autofree gchar *sn = NULL; g_autofree gchar *sr = NULL; @@ -251,12 +276,22 @@ fu_nvme_device_parse_cns (FuNvmeDevice *self, const guint8 *buf, gsize sz, GErro return FALSE; } + /* firmware update granularity (FWUG) */ + fwug = buf[319]; + 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; 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_guid (FU_DEVICE (self), gu); + /* 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 * component ID looks plausible and the GUID is "sane" */ @@ -270,24 +305,6 @@ fu_nvme_device_parse_cns (FuNvmeDevice *self, const guint8 *buf, gsize sz, GErro return TRUE; } -static guint -fu_nvme_device_pci_slot_depth (FuNvmeDevice *self) -{ - GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (self)); - g_autoptr(GUdevDevice) device_tmp = NULL; - - device_tmp = g_udev_device_get_parent_with_subsystem (udev_device, "pci", NULL); - if (device_tmp == NULL) - return 0; - for (guint i = 0; i < 0xff; i++) { - g_autoptr(GUdevDevice) parent = g_udev_device_get_parent (device_tmp); - if (parent == NULL) - return i; - g_set_object (&device_tmp, parent); - } - return 0; -} - static void fu_nvme_device_dump (const gchar *title, const guint8 *buf, gsize sz) { @@ -334,10 +351,15 @@ fu_nvme_device_probe (FuUdevDevice *device, GError **error) return FALSE; /* look at the PCI depth to work out if in an external enclosure */ - self->pci_depth = fu_nvme_device_pci_slot_depth (self); + self->pci_depth = fu_udev_device_get_slot_depth (device, "pci"); if (self->pci_depth <= 2) fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); + /* all devices need at least a warm reset, but some quirked drives + * need a full "cold" shutdown and startup */ + if (!fu_device_has_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + return TRUE; } @@ -375,12 +397,21 @@ static gboolean fu_nvme_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) { FuNvmeDevice *self = FU_NVME_DEVICE (device); + g_autoptr(GBytes) fw2 = NULL; g_autoptr(GPtrArray) chunks = NULL; guint64 block_size = self->write_block_size > 0 ? self->write_block_size : 0x1000; + /* some vendors provide firmware files whose sizes are not multiples + * of blksz *and* the device won't accept blocks of different sizes */ + if (fu_device_has_custom_flag (device, "force-align")) { + fw2 = fu_common_bytes_align (fw, block_size, 0xff); + } else { + fw2 = g_bytes_ref (fw); + } + /* build packets */ - chunks = fu_chunk_array_new_from_bytes (fw, + chunks = fu_chunk_array_new_from_bytes (fw2, 0x00, /* start_addr */ 0x00, /* page_sz */ block_size); /* block size */ @@ -422,10 +453,6 @@ fu_nvme_device_set_quirk_kv (FuDevice *device, GError **error) { FuNvmeDevice *self = FU_NVME_DEVICE (device); - if (g_strcmp0 (key, "NvmeVersionFormat") == 0) { - self->version_format = g_strdup (value); - return TRUE; - } if (g_strcmp0 (key, "NvmeBlockSize") == 0) { self->write_block_size = fu_common_strtoull (value); return TRUE; @@ -443,7 +470,6 @@ fu_nvme_device_init (FuNvmeDevice *self) { fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); fu_device_set_summary (FU_DEVICE (self), "NVM Express Solid State Drive"); fu_device_add_icon (FU_DEVICE (self), "drive-harddisk"); } @@ -451,8 +477,6 @@ fu_nvme_device_init (FuNvmeDevice *self) static void fu_nvme_device_finalize (GObject *object) { - FuNvmeDevice *self = FU_NVME_DEVICE (object); - g_free (self->version_format); G_OBJECT_CLASS (fu_nvme_device_parent_class)->finalize (object); } diff --git a/plugins/nvme/fu-plugin-nvme.c b/plugins/nvme/fu-plugin-nvme.c index 21703b9ca..25ef68f9f 100644 --- a/plugins/nvme/fu-plugin-nvme.c +++ b/plugins/nvme/fu-plugin-nvme.c @@ -31,7 +31,9 @@ fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **er void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_udev_subsystem (plugin, "nvme"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.nvmexpress"); } gboolean diff --git a/plugins/nvme/meson.build b/plugins/nvme/meson.build index bc5d28137..614ccc609 100644 --- a/plugins/nvme/meson.build +++ b/plugins/nvme/meson.build @@ -1,8 +1,16 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginNvme"'] +install_data([ + 'nvme.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_nvme', + fu_hash, sources : [ 'fu-plugin-nvme.c', + 'fu-nvme-common.c', 'fu-nvme-device.c', ], include_directories : [ @@ -16,6 +24,9 @@ shared_module('fu_plugin_nvme', cargs, '-DLOCALSTATEDIR="' + localstatedir + '"', ], + link_with : [ + libfwupdprivate, + ], dependencies : [ plugin_deps, efivar, @@ -29,6 +40,7 @@ if get_option('tests') 'nvme-self-test', sources : [ 'fu-self-test.c', + 'fu-nvme-common.c', 'fu-nvme-device.c', ], include_directories : [ @@ -42,7 +54,6 @@ if get_option('tests') efivar, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs diff --git a/plugins/nvme/nvme.quirk b/plugins/nvme/nvme.quirk new file mode 100644 index 000000000..add862356 --- /dev/null +++ b/plugins/nvme/nvme.quirk @@ -0,0 +1,3 @@ +# Phison +[DeviceInstanceId=NVME\VEN_1987] +Flags = force-align,needs-shutdown diff --git a/plugins/redfish/README.md b/plugins/redfish/README.md index a27563599..7157dd17a 100644 --- a/plugins/redfish/README.md +++ b/plugins/redfish/README.md @@ -10,6 +10,22 @@ simple and secure management of modern scalable platform hardware. By specifying a RESTful interface and utilizing JSON and OData, Redfish helps customers integrate solutions within their existing tool chains. +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: + + * org.dmtf.redfish + +GUID Generation +--------------- + +These devices use the provided GUID provided in the `SoftwareId` parameter +without modification. Devices without GUIDs are not supported. + Setting Service IP Manually --------------------------- diff --git a/plugins/redfish/fu-plugin-redfish.c b/plugins/redfish/fu-plugin-redfish.c index 1cd90c397..60ca60ece 100644 --- a/plugins/redfish/fu-plugin-redfish.c +++ b/plugins/redfish/fu-plugin-redfish.c @@ -117,6 +117,8 @@ fu_plugin_init (FuPlugin *plugin) { FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); data->client = fu_redfish_client_new (); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.dmtf.redfish"); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } void diff --git a/plugins/redfish/meson.build b/plugins/redfish/meson.build index 288f614e4..a4cdba558 100644 --- a/plugins/redfish/meson.build +++ b/plugins/redfish/meson.build @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginRedfish"'] shared_module('fu_plugin_redfish', + fu_hash, sources : [ 'fu-plugin-redfish.c', 'fu-redfish-client.c', @@ -13,6 +14,9 @@ shared_module('fu_plugin_redfish', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, @@ -44,7 +48,6 @@ if get_option('tests') libjsonglib, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs diff --git a/plugins/rts54hid/README.md b/plugins/rts54hid/README.md index 835dc9b29..1670bc323 100644 --- a/plugins/rts54hid/README.md +++ b/plugins/rts54hid/README.md @@ -10,3 +10,35 @@ 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&REV_0001` + * `USB\VID_0BDA&PID_1100` + * `USB\VID_0BDA` + +Child I²C devices are created using the device number as a suffix, for instance: + + * `USB\VID_0BDA&PID_1100&I2C_01` + +Quirk use +--------- +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|------------------------|---------------------------------------------|-----------------------| +| `Rts54SlaveAddr` | The slave address of a child module. | 1.1.3 | +| `Rts54I2cSpeed` | The I2C speed to operate at (0, 1, 2). | 1.1.3 | +| `Rts54RegisterAddrLen` | The I2C register address length of commands | 1.1.3 | diff --git a/plugins/rts54hid/fu-plugin-rts54hid.c b/plugins/rts54hid/fu-plugin-rts54hid.c index a8ebc5d03..af6699567 100644 --- a/plugins/rts54hid/fu-plugin-rts54hid.c +++ b/plugins/rts54hid/fu-plugin-rts54hid.c @@ -14,7 +14,9 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.realtek.rts54"); /* register the custom types */ g_type_ensure (FU_TYPE_RTS54HID_MODULE); diff --git a/plugins/rts54hid/meson.build b/plugins/rts54hid/meson.build index 517dde396..809a11349 100644 --- a/plugins/rts54hid/meson.build +++ b/plugins/rts54hid/meson.build @@ -7,6 +7,7 @@ install_data([ ) shared_module('fu_plugin_rts54hid', + fu_hash, sources : [ 'fu-rts54hid-device.c', 'fu-rts54hid-module.c', @@ -19,6 +20,9 @@ shared_module('fu_plugin_rts54hid', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/rts54hub/README.md b/plugins/rts54hub/README.md index 85c3e3d91..a59fe86d8 100644 --- a/plugins/rts54hub/README.md +++ b/plugins/rts54hub/README.md @@ -10,3 +10,21 @@ device using the HID update protocol. Other devices connected to the RTS54xx 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_5423&REV_0001` + * `USB\VID_0BDA&PID_5423` + * `USB\VID_0BDA` diff --git a/plugins/rts54hub/fu-plugin-rts54hub.c b/plugins/rts54hub/fu-plugin-rts54hub.c index 79c7abd4d..7300f2d1d 100644 --- a/plugins/rts54hub/fu-plugin-rts54hub.c +++ b/plugins/rts54hub/fu-plugin-rts54hub.c @@ -13,7 +13,9 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.realtek.rts54"); } gboolean diff --git a/plugins/rts54hub/meson.build b/plugins/rts54hub/meson.build index a046af90a..46b220382 100644 --- a/plugins/rts54hub/meson.build +++ b/plugins/rts54hub/meson.build @@ -7,6 +7,7 @@ install_data([ ) shared_module('fu_plugin_rts54hub', + fu_hash, sources : [ 'fu-rts54hub-device.c', 'fu-plugin-rts54hub.c', @@ -18,6 +19,9 @@ shared_module('fu_plugin_rts54hub', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/steelseries/README.md b/plugins/steelseries/README.md index 314feffef..1266c6f6d 100644 --- a/plugins/steelseries/README.md +++ b/plugins/steelseries/README.md @@ -7,3 +7,12 @@ Introduction This plugin is used to get the correct version number on SteelSeries gaming mice. These mice have updatable firmware but so far no updates are available from the vendor. + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_1038&PID_1702&REV_0001` + * `USB\VID_1038&PID_1702` + * `USB\VID_1038` diff --git a/plugins/steelseries/fu-plugin-steelseries.c b/plugins/steelseries/fu-plugin-steelseries.c index f5479c47a..78395cf48 100644 --- a/plugins/steelseries/fu-plugin-steelseries.c +++ b/plugins/steelseries/fu-plugin-steelseries.c @@ -13,6 +13,7 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); } diff --git a/plugins/steelseries/meson.build b/plugins/steelseries/meson.build index cf4fd3342..68fe15abc 100644 --- a/plugins/steelseries/meson.build +++ b/plugins/steelseries/meson.build @@ -5,6 +5,7 @@ install_data(['steelseries.quirk'], ) shared_module('fu_plugin_steelseries', + fu_hash, sources : [ 'fu-plugin-steelseries.c', 'fu-steelseries-device.c', @@ -16,6 +17,9 @@ shared_module('fu_plugin_steelseries', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/superio/README.md b/plugins/superio/README.md index c6c89f002..3be1ae935 100644 --- a/plugins/superio/README.md +++ b/plugins/superio/README.md @@ -20,3 +20,10 @@ Other useful links: * https://github.com/coreboot/coreboot/blob/master/util/superiotool/superiotool.h * https://github.com/flashrom/flashrom/blob/master/it85spi.c * http://wiki.laptop.org/go/Ec_specification + +GUID Generation +--------------- + +These devices use a custom GUID generated using the SuperIO chipset name: + + * `SuperIO-$(chipset)`, for example `SuperIO-IT8512` diff --git a/plugins/superio/fu-plugin-superio.c b/plugins/superio/fu-plugin-superio.c index 27aa014ee..a8f1ed24e 100644 --- a/plugins/superio/fu-plugin-superio.c +++ b/plugins/superio/fu-plugin-superio.c @@ -19,8 +19,7 @@ fu_plugin_superio_coldplug_chipset (FuPlugin *plugin, const gchar *chipset, GErr g_autoptr(FuDeviceLocker) locker = NULL; g_autofree gchar *key = g_strdup_printf ("SuperIO=%s", chipset); guint64 id; - guint64 data_port; - guint64 cmd_port; + guint64 port; /* get ID we need for the chipset */ id = fu_plugin_lookup_quirk_by_id_as_uint64 (plugin, key, "Id"); @@ -32,28 +31,18 @@ fu_plugin_superio_coldplug_chipset (FuPlugin *plugin, const gchar *chipset, GErr return FALSE; } - /* allow using a custom data port */ - data_port = fu_plugin_lookup_quirk_by_id_as_uint64 (plugin, key, "DataPort"); - if (data_port > 0xff) { + /* set address */ + port = fu_plugin_lookup_quirk_by_id_as_uint64 (plugin, key, "Port"); + if (port == 0x0 || port > 0xffff) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "SuperIO chip %s has invalid DataPort", chipset); - return FALSE; - } - - /* allow using a custom command port */ - cmd_port = fu_plugin_lookup_quirk_by_id_as_uint64 (plugin, key, "CmdPort"); - if (cmd_port > 0xff) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "SuperIO chip %s has invalid CmdPort", chipset); + "SuperIO chip %s has invalid Port", chipset); return FALSE; } /* create device and unlock */ - dev = fu_superio_device_new (chipset, id, data_port, cmd_port); + dev = fu_superio_device_new (chipset, id, port); locker = fu_device_locker_new (dev, error); if (locker == NULL) return FALSE; @@ -73,6 +62,12 @@ fu_plugin_superio_coldplug_chipsets (FuPlugin *plugin, const gchar *str, GError return TRUE; } +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { diff --git a/plugins/superio/fu-superio-common.c b/plugins/superio/fu-superio-common.c new file mode 100644 index 000000000..85ea2ca00 --- /dev/null +++ b/plugins/superio/fu-superio-common.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-superio-common.h" + +gboolean +fu_superio_outb (gint fd, guint16 port, guint8 data, GError **error) +{ + if (pwrite (fd, &data, 1, (goffset) port) != 1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write to port %04x: %s", + (guint) port, + strerror (errno)); + return FALSE; + } + return TRUE; +} + +gboolean +fu_superio_inb (gint fd, guint16 port, guint8 *data, GError **error) +{ + if (pread (fd, data, 1, (goffset) port) != 1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to read from port %04x: %s", + (guint) port, + strerror (errno)); + return FALSE; + } + return TRUE; +} + +gboolean +fu_superio_regval (gint fd, guint16 port, guint8 addr, + guint8 *data, GError **error) +{ + if (!fu_superio_outb (fd, port, addr, error)) + return FALSE; + if (!fu_superio_inb (fd, port + 1, data, error)) + return FALSE; + return TRUE; +} + +gboolean +fu_superio_regval16 (gint fd, guint16 port, guint8 addr, + guint16 *data, GError **error) +{ + guint8 msb; + guint8 lsb; + if (!fu_superio_regval (fd, port, addr, &msb, error)) + return FALSE; + if (!fu_superio_regval (fd, port, addr + 1, &lsb, error)) + return FALSE; + *data = ((guint16) msb << 8) | (guint16) lsb; + return TRUE; +} + +gboolean +fu_superio_regwrite (gint fd, guint16 port, guint8 addr, + guint8 data, GError **error) +{ + if (!fu_superio_outb (fd, port, addr, error)) + return FALSE; + if (!fu_superio_outb (fd, port + 1, data, error)) + return FALSE; + return TRUE; +} + +gboolean +fu_superio_set_ldn (gint fd, guint16 port, guint8 ldn, GError **error) +{ + return fu_superio_regwrite (fd, port, LDN_SEL, ldn, error); +} + +gboolean +fu_superio_regdump (gint fd, guint16 port, guint8 ldn, GError **error) +{ + g_autofree gchar *title = NULL; + guint8 buf[0xff] = { 0x00 }; + + /* set LDN */ + if (!fu_superio_set_ldn (fd, port, ldn, error)) + return FALSE; + for (guint i = 0x00; i < 0xff; i++) { + if (!fu_superio_regval (fd, port, i, &buf[i], error)) + return FALSE; + } + title = g_strdup_printf ("PORT:0x%04x LDN:0x%02x", port, ldn); + fu_common_dump_raw (G_LOG_DOMAIN, title, buf, 0x100); + return TRUE; +} diff --git a/plugins/superio/fu-superio-common.h b/plugins/superio/fu-superio-common.h new file mode 100644 index 000000000..ed2b9d2a4 --- /dev/null +++ b/plugins/superio/fu-superio-common.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_SUPERIO_COMMON_H +#define __FU_SUPERIO_COMMON_H + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define LDN_SEL 0x07 /* LDN select register */ +#define MAXLDN 0x14 /* maximum LDN */ + +gboolean fu_superio_outb (gint fd, + guint16 port, + guint8 data, + GError **error); +gboolean fu_superio_inb (gint fd, + guint16 port, + guint8 *data, + GError **error); +gboolean fu_superio_regval (gint fd, + guint16 port, + guint8 addr, + guint8 *data, + GError **error); +gboolean fu_superio_regval16 (gint fd, + guint16 port, + guint8 addr, + guint16 *data, + GError **error); +gboolean fu_superio_regwrite (gint fd, + guint16 port, + guint8 addr, + guint8 data, + GError **error); +gboolean fu_superio_regdump (gint fd, + guint16 port, + guint8 ldn, + GError **error); +gboolean fu_superio_set_ldn (gint fd, + guint16 port, + guint8 ldn, + GError **error); + +G_END_DECLS + +#endif /* __FU_SUPERIO_COMMON_H */ diff --git a/plugins/superio/fu-superio-device.c b/plugins/superio/fu-superio-device.c index 304dd1706..2e882c9cd 100644 --- a/plugins/superio/fu-superio-device.c +++ b/plugins/superio/fu-superio-device.c @@ -12,19 +12,26 @@ #include +#include "fu-superio-common.h" #include "fu-superio-device.h" -#define FU_PLUGIN_SUPERIO_TIMEOUT 5 /* s */ +#define FU_PLUGIN_SUPERIO_TIMEOUT 0.25 /* s */ #define KB_IBF (1 << 1) /* i/p buffer full */ #define KB_OBF (1 << 0) /* o/p buffer full */ +#define SIO_CMD_GET_PARAM 0x80 +#define SIO_CMD_SET_PARAM 0x81 +#define SIO_CMD_GET_NAME_STR 0x92 +#define SIO_CMD_GET_VERSION_STR 0x93 + struct _FuSuperioDevice { FuDevice parent_instance; gint fd; gchar *chipset; - guint16 data_port; - guint16 cmd_port; + guint16 port; + guint16 pm1_iobad0; + guint16 pm1_iobad1; guint16 id; guint32 size; }; @@ -38,60 +45,21 @@ fu_superio_device_to_string (FuDevice *device, GString *str) g_string_append (str, " FuSuperioDevice:\n"); g_string_append_printf (str, " fd:\t\t\t%i\n", self->fd); g_string_append_printf (str, " chipset:\t\t%s\n", self->chipset); - g_string_append_printf (str, " data-port:\t\t0x%04x\n", (guint) self->data_port); - g_string_append_printf (str, " cmd-port:\t\t0x%04x\n", (guint) self->cmd_port); g_string_append_printf (str, " id:\t\t\t0x%04x\n", (guint) self->id); + g_string_append_printf (str, " port:\t\t0x%04x\n", (guint) self->port); + g_string_append_printf (str, " pm1-iobad0:\t\t0x%04x\n", (guint) self->pm1_iobad0); + g_string_append_printf (str, " pm1-iobad1:\t\t0x%04x\n", (guint) self->pm1_iobad1); g_string_append_printf (str, " size:\t\t0x%04x\n", (guint) self->size); } -static gboolean -fu_superio_device_outb (FuSuperioDevice *self, guint16 port, guint8 data, GError **error) -{ - if (pwrite (self->fd, &data, 1, (goffset) port) != 1) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write to port %04x: %s", - (guint) port, - strerror (errno)); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_superio_device_inb (FuSuperioDevice *self, guint16 port, guint8 *data, GError **error) -{ - if (pread (self->fd, data, 1, (goffset) port) != 1) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to read from port %04x: %s", - (guint) port, - strerror (errno)); - return FALSE; - } - return TRUE; -} - static guint16 fu_superio_device_check_id (FuSuperioDevice *self, GError **error) { - guint8 msb; - guint8 lsb; guint16 id_tmp; - if (!fu_superio_device_outb (self, 0x2e, 0x20, error)) - return FALSE; - if (!fu_superio_device_inb (self, 0x2f, &msb, error)) - return FALSE; - if (!fu_superio_device_outb (self, 0x2e, 0x21, error)) - return FALSE; - if (!fu_superio_device_inb (self, 0x2f, &lsb, error)) - return FALSE; - /* check matches */ - id_tmp = ((guint16) msb << 8) | (guint16) lsb; + if (!fu_superio_regval16 (self->fd, self->port, 0x20, &id_tmp, error)) + return FALSE; if (self->id != id_tmp) { g_set_error (error, G_IO_ERROR, @@ -110,7 +78,7 @@ fu_superio_device_wait_for (FuSuperioDevice *self, guint8 mask, gboolean set, GE g_autoptr(GTimer) timer = g_timer_new (); do { guint8 status = 0x00; - if (!fu_superio_device_inb (self, self->cmd_port, &status, error)) + if (!fu_superio_inb (self->fd, self->pm1_iobad1, &status, error)) return FALSE; if (g_timer_elapsed (timer, NULL) > FU_PLUGIN_SUPERIO_TIMEOUT) break; @@ -127,27 +95,25 @@ fu_superio_device_wait_for (FuSuperioDevice *self, guint8 mask, gboolean set, GE } static gboolean -fu_superio_device_ec_cmd (FuSuperioDevice *self, guint8 data, GError **error) -{ - if (!fu_superio_device_wait_for (self, KB_IBF, FALSE, error)) - return FALSE; - return fu_superio_device_outb (self, self->cmd_port, data, error); -} - -static gboolean -fu_superio_device_ec_read (FuSuperioDevice *self, guint8 *data, GError **error) +fu_superio_device_ec_read (FuSuperioDevice *self, + guint16 port, + guint8 *data, + GError **error) { if (!fu_superio_device_wait_for (self, KB_OBF, TRUE, error)) return FALSE; - return fu_superio_device_inb (self, self->data_port, data, error); + return fu_superio_inb (self->fd, port, data, error); } static gboolean -fu_superio_device_ec_write (FuSuperioDevice *self, guint8 data, GError **error) +fu_superio_device_ec_write (FuSuperioDevice *self, + guint16 port, + guint8 data, + GError **error) { if (!fu_superio_device_wait_for (self, KB_IBF, FALSE, error)) return FALSE; - return fu_superio_device_outb (self, self->data_port, data, error); + return fu_superio_outb (self->fd, port, data, error); } static gboolean @@ -157,11 +123,11 @@ fu_superio_device_ec_flush (FuSuperioDevice *self, GError **error) g_autoptr(GTimer) timer = g_timer_new (); do { guint8 unused = 0; - if (!fu_superio_device_inb (self, self->cmd_port, &status, error)) + if (!fu_superio_inb (self->fd, self->pm1_iobad1, &status, error)) return FALSE; if ((status & KB_OBF) == 0) break; - if (!fu_superio_device_inb (self, self->data_port, &unused, error)) + if (!fu_superio_inb (self->fd, self->pm1_iobad0, &unused, error)) return FALSE; if (g_timer_elapsed (timer, NULL) > FU_PLUGIN_SUPERIO_TIMEOUT) { g_set_error_literal (error, @@ -177,22 +143,22 @@ fu_superio_device_ec_flush (FuSuperioDevice *self, GError **error) static gboolean fu_superio_device_ec_get_param (FuSuperioDevice *self, guint8 param, guint8 *data, GError **error) { - if (!fu_superio_device_ec_cmd (self, 0x80, error)) + if (!fu_superio_device_ec_write (self, self->pm1_iobad1, SIO_CMD_GET_PARAM, error)) return FALSE; - if (!fu_superio_device_ec_write (self, param, error)) + if (!fu_superio_device_ec_write (self, self->pm1_iobad0, param, error)) return FALSE; - return fu_superio_device_ec_read (self, data, error); + return fu_superio_device_ec_read (self, self->pm1_iobad0, data, error); } #if 0 static gboolean fu_superio_device_ec_set_param (FuSuperioDevice *self, guint8 param, guint8 data, GError **error) { - if (!fu_superio_device_ec_cmd (self, 0x81, error)) + if (!fu_superio_device_ec_write (self, self->pm1_iobad1, SIO_CMD_SET_PARAM, error)) return FALSE; - if (!fu_superio_device_ec_write (self, param, error)) + if (!fu_superio_device_ec_write (self, self->pm1_iobad0, param, error)) return FALSE; - return fu_superio_device_ec_write (self, data, error); + return fu_superio_device_ec_write (self, self->pm1_iobad0, data, error); } #endif @@ -200,11 +166,11 @@ static gchar * fu_superio_device_ec_get_str (FuSuperioDevice *self, guint8 idx, GError **error) { GString *str = g_string_new (NULL); - if (!fu_superio_device_ec_cmd (self, idx, error)) + if (!fu_superio_device_ec_write (self, self->pm1_iobad1, idx, error)) return NULL; for (guint i = 0; i < 0xff; i++) { guint8 c = 0; - if (!fu_superio_device_ec_read (self, &c, error)) + if (!fu_superio_device_ec_read (self, self->pm1_iobad0, &c, error)) return NULL; if (c == '$') break; @@ -248,35 +214,121 @@ fu_superio_device_probe (FuDevice *device, GError **error) } static gboolean -fu_superio_device_setup (FuDevice *device, GError **error) +fu_superio_device_setup_it85xx (FuSuperioDevice *self, GError **error) { - FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); guint8 size_tmp = 0; g_autofree gchar *name = NULL; g_autofree gchar *version = NULL; + /* get EC size */ + if (!fu_superio_device_ec_flush (self, error)) { + g_prefix_error (error, "failed to flush: "); + return FALSE; + } + if (!fu_superio_device_ec_get_param (self, 0xe5, &size_tmp, error)) { + g_prefix_error (error, "failed to get EC size: "); + return FALSE; + } + self->size = ((guint32) size_tmp) << 10; + + /* get EC strings */ + name = fu_superio_device_ec_get_str (self, SIO_CMD_GET_NAME_STR, error); + if (name == NULL) { + g_prefix_error (error, "failed to get EC name: "); + return FALSE; + } + fu_device_set_name (FU_DEVICE (self), name); + version = fu_superio_device_ec_get_str (self, SIO_CMD_GET_VERSION_STR, error); + if (version == NULL) { + g_prefix_error (error, "failed to get EC version: "); + return FALSE; + } + fu_device_set_version (FU_DEVICE (self), version); + return TRUE; +} + +static gboolean +fu_superio_device_setup_it89xx (FuSuperioDevice *self, GError **error) +{ + guint8 version_tmp[2] = { 0x00 }; + g_autofree gchar *version = NULL; + + /* get version */ + if (!fu_superio_device_ec_flush (self, error)) { + g_prefix_error (error, "failed to flush: "); + return FALSE; + } + if (!fu_superio_device_ec_get_param (self, 0x00, &version_tmp[0], error)) { + g_prefix_error (error, "failed to get version major: "); + return FALSE; + } + if (!fu_superio_device_ec_get_param (self, 0x01, &version_tmp[1], error)) { + g_prefix_error (error, "failed to get version minor: "); + return FALSE; + } + version = g_strdup_printf ("%02u.%02u", version_tmp[0], version_tmp[1]); + fu_device_set_version (FU_DEVICE (self), version); + + /* FIXME: hardcoded */ + self->size = 0x20000; + return TRUE; +} + +static gboolean +fu_superio_device_setup (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + /* check ID is correct */ if (!fu_superio_device_check_id (self, error)) { g_prefix_error (error, "failed to probe id: "); return FALSE; } - /* get EC size */ - if (!fu_superio_device_ec_flush (self, error)) - return FALSE; - if (!fu_superio_device_ec_get_param (self, 0xe5, &size_tmp, error)) - return FALSE; - self->size = ((guint32) size_tmp) << 10; + /* dump LDNs */ + if (g_getenv ("FWUPD_SUPERIO_VERBOSE") != NULL) { + for (guint j = 0; j < MAXLDN; j++) { + if (!fu_superio_regdump (self->fd, self->port, j, error)) + return FALSE; + } + } - /* get EC strings */ - name = fu_superio_device_ec_get_str (self, 0x92, error); - if (name == NULL) + /* set Power Management I/F Channel 1 LDN */ + if (!fu_superio_set_ldn (self->fd, self->port, 0x11, error)) return FALSE; - fu_device_set_name (device, name); - version = fu_superio_device_ec_get_str (self, 0x93, error); - if (version == NULL) + + /* get the PM1 IOBAD0 address */ + if (!fu_superio_regval16 (self->fd, self->port, 0x60, &self->pm1_iobad0, error)) return FALSE; - fu_device_set_version (device, version); + + /* get the PM1 IOBAD1 address */ + if (!fu_superio_regval16 (self->fd, self->port, 0x62, &self->pm1_iobad1, error)) + return FALSE; + + /* dump PMC register map */ + if (g_getenv ("FWUPD_SUPERIO_VERBOSE") != NULL) { + guint8 buf[0xff] = { 0x00 }; + for (guint i = 0x00; i < 0xff; i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_superio_device_ec_get_param (self, i, &buf[i], &error_local)) { + g_debug ("param: 0x%02x = %s", i, error_local->message); + continue; + } + } + fu_common_dump_raw (G_LOG_DOMAIN, "EC PMC Registers", buf, 0x100); + } + + /* IT85xx */ + if (self->id >> 8 == 0x85) { + if (!fu_superio_device_setup_it85xx (self, error)) + return FALSE; + } + + /* IT89xx */ + if (self->id >> 8 == 0x89) { + if (!fu_superio_device_setup_it89xx (self, error)) + return FALSE; + } /* success */ return TRUE; @@ -321,13 +373,12 @@ fu_superio_device_class_init (FuSuperioDeviceClass *klass) } FuSuperioDevice * -fu_superio_device_new (const gchar *chipset, guint16 id, guint8 data_port, guint8 cmd_port) +fu_superio_device_new (const gchar *chipset, guint16 id, guint16 port) { FuSuperioDevice *self; self = g_object_new (FU_TYPE_SUPERIO_DEVICE, NULL); self->chipset = g_strdup (chipset); self->id = id; - self->data_port = data_port > 0 ? data_port : 0x62; - self->cmd_port = cmd_port > 0 ? cmd_port : 0x66; + self->port = port; return self; } diff --git a/plugins/superio/fu-superio-device.h b/plugins/superio/fu-superio-device.h index b9288ba7d..de7722e67 100644 --- a/plugins/superio/fu-superio-device.h +++ b/plugins/superio/fu-superio-device.h @@ -16,8 +16,7 @@ G_DECLARE_FINAL_TYPE (FuSuperioDevice, fu_superio_device, FU, SUPERIO_DEVICE, Fu FuSuperioDevice *fu_superio_device_new (const gchar *chipset, guint16 id, - guint8 data_port, - guint8 cmd_port); + guint16 port); G_END_DECLS diff --git a/plugins/superio/meson.build b/plugins/superio/meson.build index 57edaf93c..06b094342 100644 --- a/plugins/superio/meson.build +++ b/plugins/superio/meson.build @@ -5,9 +5,11 @@ install_data(['superio.quirk'], ) shared_module('fu_plugin_superio', + fu_hash, sources : [ 'fu-plugin-superio.c', 'fu-superio-device.c', + 'fu-superio-common.c', ], include_directories : [ include_directories('../..'), @@ -16,6 +18,9 @@ shared_module('fu_plugin_superio', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/superio/superio.quirk b/plugins/superio/superio.quirk index 019c63ca3..d1fa268c0 100644 --- a/plugins/superio/superio.quirk +++ b/plugins/superio/superio.quirk @@ -6,23 +6,42 @@ SuperioChipsets=IT8587 [HwId=f00d8c4e-dce2-51c3-89d6-6cbc5fc5cdbb] SuperioChipsets=IT8587 +# Star LabTop Mk3 +[HwId=3dc52d2c-9e9b-5ba5-b10d-9ba1eb11dacc] +SuperioChipsets=IT8987 + +# Star Lite Mk2 +[HwId=d1a64840-4307-58fb-a62c-de28a07c0151] +SuperioChipsets=IT8987 + [SuperIO=IT8510] Id=0x8510 +Port=0x2e [SuperIO=IT8511] Id=0x8511 +Port=0x2e [SuperIO=IT8512] Id=0x8512 +Port=0x2e [SuperIO=IT8513] Id=0x8513 +Port=0x2e [SuperIO=IT8516] Id=0x8516 +Port=0x2e [SuperIO=IT8518] Id=0x8518 +Port=0x2e [SuperIO=IT8587] Id=0x8587 +Port=0x2e + +[SuperIO=IT8987] +Id=0x8987 +Port=0x4e diff --git a/plugins/synapticsmst/README.md b/plugins/synapticsmst/README.md index e50a7c996..1428db78f 100644 --- a/plugins/synapticsmst/README.md +++ b/plugins/synapticsmst/README.md @@ -3,6 +3,26 @@ This plugin supports querying and flashing Synaptics MST hubs used in Dell systems and docks. +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.synaptics.mst + +GUID Generation +--------------- + +These devices use custom GUID values, e.g. + + * `MST-$(device_kind)-$(chip-ID)-$(board-ID)` + +Please refer to the plugin source for more details about how the GUID is +constructed for specific hardware. + ## Requirements ### (Kernel) DP Aux Interface Kernel 4.6 introduced an DRM DP Aux interface for manipulation of the registers @@ -41,18 +61,19 @@ Payloads can be flashed just like any other plugin from LVFS. ## Supported devices Not all Dell systems or accessories contain MST hubs. Here is a sample list of systems known to support them however: -1. Dell WD15 dock -2. Dell TB16 dock -3. Latitude E5570 -4. Latitude E5470 -5. Latitude E5270 -6. Latitude E7470 -7. Latitude E7270 -8. Latitude E7450 -9. Latitude E7250 -10. Latitude E5550 -11. Latitude E5450 -12. Latitude E5250 -13. Latitude Rugged 5414 -14. Latitude Rugged 7214 -15. Latitude Rugged 7414 + * Dell WD15 dock + * Dell TB16 dock + * Dell TB18DC + * Latitude E5570 + * Latitude E5470 + * Latitude E5270 + * Latitude E7470 + * Latitude E7270 + * Latitude E7450 + * Latitude E7250 + * Latitude E5550 + * Latitude E5450 + * Latitude E5250 + * Latitude Rugged 5414 + * Latitude Rugged 7214 + * Latitude Rugged 7414 diff --git a/plugins/synapticsmst/fu-plugin-synapticsmst.c b/plugins/synapticsmst/fu-plugin-synapticsmst.c index 780064834..fa86b2144 100644 --- a/plugins/synapticsmst/fu-plugin-synapticsmst.c +++ b/plugins/synapticsmst/fu-plugin-synapticsmst.c @@ -171,6 +171,10 @@ fu_plugin_synaptics_add_device (FuPlugin *plugin, fu_plugin_device_add (plugin, dev); fu_plugin_cache_add (plugin, dev_id_str, dev); + + /* inhibit the idle sleep of the daemon */ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE, + "SynapticsMST can cause the screen to flash when probing"); return TRUE; } @@ -467,4 +471,6 @@ fu_plugin_init (FuPlugin *plugin) { /* make sure dell is already coldplugged */ fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "dell"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.synaptics.mst"); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } diff --git a/plugins/synapticsmst/meson.build b/plugins/synapticsmst/meson.build index 3a93115b2..0f1abdbf0 100644 --- a/plugins/synapticsmst/meson.build +++ b/plugins/synapticsmst/meson.build @@ -5,6 +5,7 @@ install_data(['synapticsmst.quirk'], ) shared_module('fu_plugin_synapticsmst', + fu_hash, sources : [ 'fu-plugin-synapticsmst.c', 'synapticsmst-common.c', @@ -18,8 +19,11 @@ shared_module('fu_plugin_synapticsmst', install : true, install_dir: plugin_dir, c_args : [ - cargs, - ], + cargs, + ], + link_with : [ + libfwupdprivate, + ], dependencies : [ plugin_deps, ], @@ -47,7 +51,6 @@ if get_option('tests') valgrind, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : [ diff --git a/plugins/test/README.md b/plugins/test/README.md index bef15c829..4c8891cfb 100644 --- a/plugins/test/README.md +++ b/plugins/test/README.md @@ -5,3 +5,9 @@ Introduction ------------ This plugin is used when running the self tests in the fwupd project. + +GUID Generation +--------------- + +The devices created by this plugin use hardcoded GUIDs that do not correspond +to any kind of DeviceInstanceId values. diff --git a/plugins/test/fu-plugin-test.c b/plugins/test/fu-plugin-test.c index 2d14c96d3..9c7875943 100644 --- a/plugins/test/fu-plugin-test.c +++ b/plugins/test/fu-plugin-test.c @@ -15,6 +15,11 @@ struct FuPluginData { void fu_plugin_init (FuPlugin *plugin) { + if (g_strcmp0 (g_getenv ("FWUPD_PLUGIN_TEST"), "build-hash") == 0) + fu_plugin_set_build_hash (plugin, "invalid"); + else + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.acme.test"); fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); g_debug ("init"); } diff --git a/plugins/test/meson.build b/plugins/test/meson.build index 7f07a596c..c473ab4b0 100644 --- a/plugins/test/meson.build +++ b/plugins/test/meson.build @@ -6,6 +6,7 @@ if get_option('plugin_dummy') endif shared_module('fu_plugin_test', + fu_hash, sources : [ 'fu-plugin-test.c', ], @@ -16,6 +17,9 @@ shared_module('fu_plugin_test', ], install : install_dummy, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c b/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c index 4430347af..4bfc20d01 100644 --- a/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c +++ b/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c @@ -320,6 +320,7 @@ fu_plugin_init (FuPlugin *plugin) /* make sure it's tried to coldplug */ fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "thunderbolt"); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } void diff --git a/plugins/thunderbolt-power/meson.build b/plugins/thunderbolt-power/meson.build index 59257c6ed..747c782f6 100644 --- a/plugins/thunderbolt-power/meson.build +++ b/plugins/thunderbolt-power/meson.build @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginThunderbolt"'] fu_plugin_thunderbolt_power = shared_module('fu_plugin_thunderbolt_power', + fu_hash, sources : [ 'fu-plugin-thunderbolt-power.c', ], @@ -11,6 +12,9 @@ fu_plugin_thunderbolt_power = shared_module('fu_plugin_thunderbolt_power', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/thunderbolt/README.md b/plugins/thunderbolt/README.md index 5ceeca54d..8b235593c 100644 --- a/plugins/thunderbolt/README.md +++ b/plugins/thunderbolt/README.md @@ -9,6 +9,28 @@ allows the connection of external peripherals to a computer. Versions 1 and 2 use the same connector as Mini DisplayPort (MDP), whereas version 3 uses USB Type-C. +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format, with vendor specific header. + +This plugin supports the following protocol ID: + + * com.intel.thunderbolt + +GUID Generation +--------------- + +These devices use a custom GUID generation scheme. +When the device is in "safe mode" the GUID is hardcoded using: + + * `TBT-safemode` + +... and when in runtime mode the GUID is: + + * `TBT-$(vid)$(pid)-native` when native, and `TBT-$(vid)$(pid)` otherwise. + Runtime Power Management ------------------------ diff --git a/plugins/thunderbolt/fu-plugin-thunderbolt.c b/plugins/thunderbolt/fu-plugin-thunderbolt.c index 24936a423..81d545447 100644 --- a/plugins/thunderbolt/fu-plugin-thunderbolt.c +++ b/plugins/thunderbolt/fu-plugin-thunderbolt.c @@ -193,23 +193,45 @@ fu_plugin_thunderbolt_udev_get_version (GUdevDevice *udevice) static gboolean fu_plugin_thunderbolt_is_native (GUdevDevice *udevice, gboolean *is_native, GError **error) { + gsize nr_chunks; g_autoptr(GFile) nvmem = NULL; g_autoptr(GBytes) controller_fw = NULL; - gchar *content; - gsize length; + g_autoptr(GInputStream) istr = NULL; nvmem = fu_plugin_thunderbolt_find_nvmem (udevice, TRUE, error); if (nvmem == NULL) return FALSE; - if (!g_file_load_contents (nvmem, NULL, &content, &length, NULL, error)) + /* 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; + controller_fw = g_input_stream_read_bytes (istr, + nr_chunks * FU_TBT_CHUNK_SZ, + NULL, error); + if (controller_fw == NULL) return FALSE; - controller_fw = g_bytes_new_take (content, length); + return fu_thunderbolt_image_controller_is_native (controller_fw, + is_native, + error); +} - return fu_plugin_thunderbolt_controller_is_native (controller_fw, - is_native, - error); +static gboolean +fu_plugin_thunderbolt_can_update (GUdevDevice *udevice) +{ + g_autoptr(GError) nvmem_error = NULL; + g_autoptr(GFile) non_active_nvmem = NULL; + + non_active_nvmem = fu_plugin_thunderbolt_find_nvmem (udevice, FALSE, + &nvmem_error); + if (non_active_nvmem == NULL) { + g_debug ("%s", nvmem_error->message); + return FALSE; + } + + return TRUE; } static void @@ -293,22 +315,29 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device) is_safemode ? "True" : "False"); } if (!is_safemode) { - if (is_host) { - g_autoptr(GError) error_local = NULL; - if (!fu_plugin_thunderbolt_is_native (device, &is_native, &error_local)) { - g_warning ("failed to get native mode status: %s", error_local->message); - return; + if (fu_plugin_thunderbolt_can_update (device)) { + if (is_host) { + g_autoptr(GError) native_error = NULL; + if (!fu_plugin_thunderbolt_is_native (device, + &is_native, + &native_error)) { + g_warning ("failed to get native mode status: %s", + native_error->message); + return; + } + fu_plugin_add_report_metadata (plugin, + "ThunderboltNative", + is_native ? "True" : "False"); } - fu_plugin_add_report_metadata (plugin, - "ThunderboltNative", - is_native ? "True" : "False"); + vendor_id = g_strdup_printf ("TBT:0x%04X", (guint) vid); + device_id = g_strdup_printf ("TBT-%04x%04x%s", + (guint) vid, + (guint) did, + is_native ? "-native" : ""); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); + } else { + fu_device_set_update_error (dev, "Missing non-active nvmem"); } - vendor_id = g_strdup_printf ("TBT:0x%04X", (guint) vid); - device_id = g_strdup_printf ("TBT-%04x%04x%s", - (guint) vid, - (guint) did, - is_native ? "-native" : ""); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); } else { fu_device_set_update_error (dev, "Device is in safe mode"); } @@ -348,6 +377,10 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device) fu_plugin_cache_add (plugin, id, dev); fu_plugin_device_add (plugin, dev); + + /* inhibit the idle sleep of the daemon */ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE, + "thunderbolt requires device wakeup"); } static void @@ -436,10 +469,7 @@ fu_plugin_thunderbolt_validate_firmware (GUdevDevice *udevice, return VALIDATION_FAILED; controller_fw = g_bytes_new_take (content, length); - - return fu_plugin_thunderbolt_validate_image (controller_fw, - blob_fw, - error); + return fu_thunderbolt_image_validate (controller_fw, blob_fw, error); } static gboolean @@ -555,9 +585,14 @@ fu_plugin_init (FuPlugin *plugin) FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); const gchar *subsystems[] = { "thunderbolt", NULL }; + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.intel.thunderbolt"); data->udev = g_udev_client_new (subsystems); g_signal_connect (data->udev, "uevent", G_CALLBACK (udev_uevent_cb), plugin); + + /* dell-dock plugin uses a slower bus for flashing */ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "dell_dock"); } void @@ -706,8 +741,7 @@ fu_plugin_update_attach (FuPlugin *plugin, return FALSE; } status = g_ascii_strtoull (attribute, NULL, 16); - if ((status == 0x00 && errno == EINVAL) || - (status == G_MAXUINT64 && errno == ERANGE)) { + if (status == G_MAXUINT64 && errno == ERANGE) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "failed to read 'nvm_authenticate: %s", diff --git a/plugins/thunderbolt/fu-self-test.c b/plugins/thunderbolt/fu-self-test.c index 37f492ff8..5cdf5a296 100644 --- a/plugins/thunderbolt/fu-self-test.c +++ b/plugins/thunderbolt/fu-self-test.c @@ -1045,40 +1045,40 @@ test_image_validation (ThunderboltTest *tt, gconstpointer user_data) g_assert_nonnull (bad_data); /* now for some testing ... this should work */ - val = fu_plugin_thunderbolt_validate_image (ctl_data, fwi_data, &error); + val = fu_thunderbolt_image_validate (ctl_data, fwi_data, &error); g_assert_no_error (error); g_assert_cmpint (val, ==, VALIDATION_PASSED); /* these all should fail */ /* valid controller, bad update data */ - val = fu_plugin_thunderbolt_validate_image (ctl_data, ctl_data, &error); + val = fu_thunderbolt_image_validate (ctl_data, ctl_data, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_READ); g_assert_cmpint (val, ==, VALIDATION_FAILED); g_debug ("expected image validation error [ctl, ctl]: %s", error->message); g_clear_error (&error); - val = fu_plugin_thunderbolt_validate_image (ctl_data, bad_data, &error); + val = fu_thunderbolt_image_validate (ctl_data, bad_data, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_READ); g_assert_cmpint (val, ==, VALIDATION_FAILED); g_debug ("expected image validation error [ctl, bad]: %s", error->message); g_clear_error (&error); /* bad controller data, valid update data */ - val = fu_plugin_thunderbolt_validate_image (fwi_data, fwi_data, &error); + val = fu_thunderbolt_image_validate (fwi_data, fwi_data, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_cmpint (val, ==, VALIDATION_FAILED); g_debug ("expected image validation error [fwi, fwi]: %s", error->message); g_clear_error (&error); - val = fu_plugin_thunderbolt_validate_image (bad_data, fwi_data, &error); + val = fu_thunderbolt_image_validate (bad_data, fwi_data, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_cmpint (val, ==, VALIDATION_FAILED); g_debug ("expected image validation error [bad, fwi]: %s", error->message); g_clear_error (&error); /* both bad */ - val = fu_plugin_thunderbolt_validate_image (bad_data, bad_data, &error); + val = fu_thunderbolt_image_validate (bad_data, bad_data, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_READ); g_assert_cmpint (val, ==, VALIDATION_FAILED); g_debug ("expected image validation error [bad, bad]: %s", error->message); diff --git a/plugins/thunderbolt/fu-thunderbolt-image.c b/plugins/thunderbolt/fu-thunderbolt-image.c index 231248141..c88aed9b0 100644 --- a/plugins/thunderbolt/fu-thunderbolt-image.c +++ b/plugins/thunderbolt/fu-thunderbolt-image.c @@ -642,9 +642,9 @@ compare_pd_existence (guint16 id, } FuPluginValidation -fu_plugin_thunderbolt_validate_image (GBytes *controller_fw, - GBytes *blob_fw, - GError **error) +fu_thunderbolt_image_validate (GBytes *controller_fw, + GBytes *blob_fw, + GError **error) { gboolean is_host; guint16 device_id; @@ -779,16 +779,18 @@ fu_plugin_thunderbolt_validate_image (GBytes *controller_fw, } gboolean -fu_plugin_thunderbolt_controller_is_native (GBytes *controller_fw, - gboolean *is_native, - GError **error) +fu_thunderbolt_image_controller_is_native (GBytes *controller_fw, + gboolean *is_native, + GError **error) { guint32 controller_sections[SECTION_COUNT] = { [DIGITAL_SECTION] = 0 }; gsize fw_size; const guint8 *fw_data = g_bytes_get_data (controller_fw, &fw_size); const FuThunderboltFwObject controller = { fw_data, fw_size, controller_sections }; - - const FuThunderboltFwLocation location = { .offset = 0x7B, .len = 1, .description = "Native", .mask = 0x20 }; - + const FuThunderboltFwLocation location = { + .offset = FU_TBT_OFFSET_NATIVE, + .len = 1, + .description = "Native", + .mask = 0x20 }; return read_bool (&location, &controller, is_native, error); } diff --git a/plugins/thunderbolt/fu-thunderbolt-image.h b/plugins/thunderbolt/fu-thunderbolt-image.h index f094f563a..ee7d000ce 100644 --- a/plugins/thunderbolt/fu-thunderbolt-image.h +++ b/plugins/thunderbolt/fu-thunderbolt-image.h @@ -15,11 +15,15 @@ typedef enum { UNKNOWN_DEVICE, } FuPluginValidation; -FuPluginValidation fu_plugin_thunderbolt_validate_image (GBytes *controller_fw, +/* byte offsets in firmware image */ +#define FU_TBT_OFFSET_NATIVE 0x7B +#define FU_TBT_CHUNK_SZ 0x40 + +FuPluginValidation fu_thunderbolt_image_validate (GBytes *controller_fw, GBytes *blob_fw, GError **error); -gboolean fu_plugin_thunderbolt_controller_is_native (GBytes *controller_fw, +gboolean fu_thunderbolt_image_controller_is_native (GBytes *controller_fw, gboolean *is_native, GError **error); diff --git a/plugins/thunderbolt/fu-thunderbolt-tool.c b/plugins/thunderbolt/fu-thunderbolt-tool.c index 79466c938..a4675aab6 100644 --- a/plugins/thunderbolt/fu-thunderbolt-tool.c +++ b/plugins/thunderbolt/fu-thunderbolt-tool.c @@ -82,7 +82,7 @@ main (int argc, char **argv) controller = g_bytes_new_take (controller_data, controller_len); } - validation = fu_plugin_thunderbolt_validate_image (controller, image, &error); + validation = fu_thunderbolt_image_validate (controller, image, &error); g_assert_no_error (error); g_assert_cmpint (validation, ==, VALIDATION_PASSED); diff --git a/plugins/thunderbolt/meson.build b/plugins/thunderbolt/meson.build index 17e81b73e..10991e5b1 100644 --- a/plugins/thunderbolt/meson.build +++ b/plugins/thunderbolt/meson.build @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginThunderbolt"'] fu_plugin_thunderbolt = shared_module('fu_plugin_thunderbolt', + fu_hash, sources : [ 'fu-plugin-thunderbolt.c', 'fu-thunderbolt-image.c', @@ -12,6 +13,9 @@ fu_plugin_thunderbolt = shared_module('fu_plugin_thunderbolt', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, @@ -33,7 +37,6 @@ executable('tbtfwucli', c_args : cargs, link_with : [ fu_plugin_thunderbolt, - fwupd, libfwupdprivate, ], dependencies : [ @@ -62,7 +65,6 @@ if get_option('tests') and umockdev.found() and gio.version().version_compare('> umockdev, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs diff --git a/plugins/udev/README.md b/plugins/udev/README.md index cf254bc4e..994769762 100644 --- a/plugins/udev/README.md +++ b/plugins/udev/README.md @@ -10,3 +10,12 @@ require booting into another operating system to apply. 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` + +Additionally, GUIDs found in OptionROMs may also be added. diff --git a/plugins/udev/fu-plugin-udev.c b/plugins/udev/fu-plugin-udev.c index cb3f0ec43..af48b1add 100644 --- a/plugins/udev/fu-plugin-udev.c +++ b/plugins/udev/fu-plugin-udev.c @@ -13,6 +13,7 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_udev_subsystem (plugin, "pci"); } @@ -84,8 +85,8 @@ fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **er return FALSE; /* did we get enough data */ - fu_device_add_flag (device, FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_icon (device, "audio-card"); + fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon (FU_DEVICE (device), "audio-card"); /* get the FW version from the rom when unlocked */ rom_fn = g_build_filename (fu_udev_device_get_sysfs_path (device), "rom", NULL); diff --git a/plugins/udev/fu-rom.c b/plugins/udev/fu-rom.c index 5f9917c45..0dc7c2dca 100644 --- a/plugins/udev/fu-rom.c +++ b/plugins/udev/fu-rom.c @@ -7,10 +7,10 @@ #include "config.h" #include -#include #include #include +#include "fu-common-guid.h" #include "fu-rom.h" static void fu_rom_finalize (GObject *object); @@ -689,7 +689,7 @@ fu_rom_load_data (FuRom *self, /* update guid */ id = g_strdup_printf ("PCI\\VEN_%04X&DEV_%04X", self->vendor_id, self->device_id); - self->guid = as_utils_guid_from_string (id); + self->guid = fu_common_guid_from_string (id); g_debug ("using %s for %s", self->guid, id); /* not known */ diff --git a/plugins/udev/meson.build b/plugins/udev/meson.build index cdadc08d2..334b14a87 100644 --- a/plugins/udev/meson.build +++ b/plugins/udev/meson.build @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginUdev"'] shared_module('fu_plugin_udev', + fu_hash, sources : [ 'fu-plugin-udev.c', 'fu-rom.c', @@ -12,6 +13,9 @@ shared_module('fu_plugin_udev', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, @@ -20,6 +24,7 @@ shared_module('fu_plugin_udev', executable( 'fu-rom-tool', + fu_hash, sources : [ 'fu-rom-tool.c', 'fu-rom.c', @@ -33,7 +38,6 @@ executable( plugin_deps, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs, @@ -59,7 +63,6 @@ if get_option('tests') plugin_deps, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs diff --git a/plugins/uefi/README.md b/plugins/uefi/README.md index c005abf7f..7f05c0afd 100644 --- a/plugins/uefi/README.md +++ b/plugins/uefi/README.md @@ -11,6 +11,28 @@ With the UpdateCapsule boot service it can be used to update system firmware. If you don't want or need this functionality you can use the `-Dplugin_uefi=false` option. +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +EFI capsule file format. + +See https://www.uefi.org/sites/default/files/resources/UEFI%20Spec%202_6.pdf +for details. + +This plugin supports the following protocol ID: + + * org.uefi.capsule + +GUID Generation +--------------- + +These devices use the UEFI GUID as provided in the ESRT. Additionally, for the +system device the `main-system-firmware` GUID is also added. + +For compatibility with Windows 10, the plugin also adds GUIDs of the form +`UEFI\RES_{$(esrt)}`. + UEFI Unlock Support ------------------- diff --git a/plugins/uefi/efi/fwup-cleanups.h b/plugins/uefi/efi/fwup-cleanups.h new file mode 100644 index 000000000..ff5328f7c --- /dev/null +++ b/plugins/uefi/efi/fwup-cleanups.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define _DEFINE_CLEANUP_FUNCTION0(Type, name, func) \ + static inline VOID name(VOID *v) \ + { \ + if (*(Type*)v) \ + func (*(Type*)v); \ + } +_DEFINE_CLEANUP_FUNCTION0(VOID *, _FreePool_p, FreePool) +#define _cleanup_free __attribute__ ((cleanup(_FreePool_p))) + +static inline VOID * +_steal_pointer(VOID *pp) +{ + VOID **ptr = (VOID **) pp; + VOID *ref = *ptr; + *ptr = NULL; + return ref; +} diff --git a/plugins/uefi/efi/fwup-common.c b/plugins/uefi/efi/fwup-common.c new file mode 100644 index 000000000..b7ff19980 --- /dev/null +++ b/plugins/uefi/efi/fwup-common.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include +#include + +#include "fwup-debug.h" +#include "fwup-common.h" + +VOID +fwup_msleep(unsigned long msecs) +{ + BS->Stall(msecs); +} + +/* + * Allocate some raw pages that aren't part of the pool allocator. + */ +VOID * +fwup_malloc_raw(UINTN size) +{ + UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0); /* page size is always 4096 */ + EFI_STATUS rc; + EFI_PHYSICAL_ADDRESS pageaddr = 0; + EFI_ALLOCATE_TYPE type = AllocateAnyPages; + + if (sizeof(VOID *) == 4) { + pageaddr = 0xffffffffULL - 8192; + type = AllocateMaxAddress; + } + + rc = uefi_call_wrapper(BS->AllocatePages, 4, type, + EfiLoaderData, pages, + &pageaddr); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not allocate %d", size); + return NULL; + } + if (sizeof(VOID *) == 4 && pageaddr > 0xffffffffULL) { + uefi_call_wrapper(BS->FreePages, 2, pageaddr, pages); + fwup_warning(L"Got bad allocation at 0x%016x", (UINT64)pageaddr); + return NULL; + } + return (VOID *)(UINTN)pageaddr; +} + +/* + * Free our raw page allocations. + */ +static EFI_STATUS +fwup_free_raw(VOID *addr, UINTN size) +{ + UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0); + return uefi_call_wrapper(BS->FreePages, 2, + (EFI_PHYSICAL_ADDRESS)(UINTN)addr, pages); +} + +VOID * +fwup_malloc (UINTN size) +{ + VOID *addr = AllocatePool(size); + if (addr == NULL) + fwup_warning(L"Could not allocate %d", size); + return addr; +} + +VOID * +fwup_malloc0 (UINTN size) +{ + VOID *addr = AllocateZeroPool(size); + if (addr == NULL) + fwup_warning(L"Could not allocate %d", size); + return addr; +} + +EFI_STATUS +fwup_time(EFI_TIME *ts) +{ + EFI_TIME_CAPABILITIES timecaps = { 0, }; + return uefi_call_wrapper(RT->GetTime, 2, ts, &timecaps); +} + +EFI_STATUS +fwup_read_file(EFI_FILE_HANDLE fh, UINT8 **buf_out, UINTN *buf_size_out) +{ + const UINTN bs = 512; + UINTN i = 0; + UINTN n_blocks = 4096; + UINT8 *buf = NULL; + + while (1) { + VOID *newb = NULL; + UINTN news = n_blocks * bs * 2; + + newb = fwup_malloc_raw(news); + if (newb == NULL) + return EFI_OUT_OF_RESOURCES; + if (buf != NULL) { + CopyMem(newb, buf, bs * n_blocks); + fwup_free_raw(buf, bs * n_blocks); + } + buf = newb; + n_blocks *= 2; + + for (; i < n_blocks; i++) { + EFI_STATUS rc; + UINTN sz = bs; + + rc = uefi_call_wrapper(fh->Read, 3, fh, &sz, &buf[i * bs]); + if (EFI_ERROR(rc)) { + fwup_free_raw(buf, bs * n_blocks); + fwup_warning(L"Could not read file: %r", rc); + return rc; + } + + if (sz != bs) { + *buf_size_out = bs * i + sz; + *buf_out = buf; + return EFI_SUCCESS; + } + } + } + return EFI_SUCCESS; +} diff --git a/plugins/uefi/efi/fwup-common.h b/plugins/uefi/efi/fwup-common.h new file mode 100644 index 000000000..9aee941c1 --- /dev/null +++ b/plugins/uefi/efi/fwup-common.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015-2016 Peter Jones + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef _FWUP_COMMON_H +#define _FWUP_COMMON_H + +#include "fwup-efi.h" + +VOID fwup_msleep (unsigned long msecs); +EFI_STATUS fwup_time (EFI_TIME *ts); +EFI_STATUS fwup_read_file (EFI_FILE_HANDLE fh, + UINT8 **buf_out, + UINTN *buf_size_out); +VOID *fwup_malloc_raw (UINTN size); + +VOID *fwup_malloc (UINTN size); +VOID *fwup_malloc0 (UINTN size); + +#define fwup_new(struct_type, n) ((struct_type*)fwup_malloc((n)*sizeof(struct_type))) +#define fwup_new0(struct_type, n) ((struct_type*)fwup_malloc0((n)*sizeof(struct_type))) + +#endif /* _FWUP_COMMON_H */ diff --git a/plugins/uefi/efi/fwup-debug.c b/plugins/uefi/efi/fwup-debug.c new file mode 100644 index 000000000..bbb2d0198 --- /dev/null +++ b/plugins/uefi/efi/fwup-debug.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include +#include + +#include "fwup-cleanups.h" +#include "fwup-debug.h" +#include "fwup-efi.h" + +static BOOLEAN debugging = FALSE; + +BOOLEAN +fwup_debug_get_enabled(VOID) +{ + return debugging; +} + +VOID +fwup_debug_set_enabled(BOOLEAN val) +{ + debugging = val; +} + +static VOID +fwupd_debug_efivar_append(CHAR16 *out1) +{ + CHAR16 *name = L"FWUPDATE_DEBUG_LOG"; + UINT32 attrs = EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + static BOOLEAN once = TRUE; + if (once) { + once = FALSE; + fwup_delete_variable(name, &fwupdate_guid); + } else { + attrs |= EFI_VARIABLE_APPEND_WRITE; + } + fwup_set_variable(name, &fwupdate_guid, out1, StrSize(out1) - sizeof(CHAR16), attrs); +} + +VOID +fwup_log(FwupLogLevel level, const char *func, const char *file, const int line, CHAR16 *fmt, ...) +{ + va_list args; + _cleanup_free CHAR16 *tmp = NULL; + + va_start(args, fmt); + tmp = VPoolPrint(fmt, args); + va_end(args); + if (tmp == NULL) { + Print(L"fwupdate: Allocation for debug log failed!\n"); + return; + } + + if (debugging) { + _cleanup_free CHAR16 *out1 = NULL; + out1 = PoolPrint(L"%a:%d:%a(): %s", file, line, func, tmp); + if (out1 == NULL) { + Print(L"fwupdate: Allocation for debug log failed!\n"); + return; + } + Print(L"%s\n", out1); + fwupd_debug_efivar_append(out1); + } else { + switch (level) { + case FWUP_DEBUG_LEVEL_DEBUG: + break; + case FWUP_DEBUG_LEVEL_WARNING: + Print(L"WARNING: %s\n", tmp); + break; + default: + Print(L"%s\n", tmp); + break; + } + } +} diff --git a/plugins/uefi/efi/fwup-debug.h b/plugins/uefi/efi/fwup-debug.h new file mode 100644 index 000000000..1e1de2198 --- /dev/null +++ b/plugins/uefi/efi/fwup-debug.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015-2016 Peter Jones + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef _FWUP_DEBUG_H +#define _FWUP_DEBUG_H + +typedef enum { + FWUP_DEBUG_LEVEL_DEBUG, + FWUP_DEBUG_LEVEL_INFO, + FWUP_DEBUG_LEVEL_WARNING, + FWUP_DEBUG_LEVEL_LAST +} FwupLogLevel; + +VOID fwup_log (FwupLogLevel level, + const char *func, + const char *file, + const int line, + CHAR16 *fmt, + ...); + +BOOLEAN fwup_debug_get_enabled (VOID); +VOID fwup_debug_set_enabled (BOOLEAN val); + +#define fwup_debug(fmt, args...) fwup_log(FWUP_DEBUG_LEVEL_DEBUG, __func__, __FILE__, __LINE__, fmt, ## args ) +#define fwup_info(fmt, args...) fwup_log(FWUP_DEBUG_LEVEL_INFO, __func__, __FILE__, __LINE__, fmt, ## args ) +#define fwup_warning(fmt, args...) fwup_log(FWUP_DEBUG_LEVEL_WARNING, __func__, __FILE__, __LINE__, fmt, ## args ) + +#endif /* _FWUP_DEBUG_H */ diff --git a/plugins/uefi/efi/fwup-efi.c b/plugins/uefi/efi/fwup-efi.c new file mode 100644 index 000000000..8da6b2021 --- /dev/null +++ b/plugins/uefi/efi/fwup-efi.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include +#include + +#include "fwup-cleanups.h" +#include "fwup-common.h" +#include "fwup-debug.h" +#include "fwup-efi.h" + +EFI_STATUS +fwup_delete_variable(CHAR16 *name, EFI_GUID *guid) +{ + EFI_STATUS rc; + UINT32 attrs = 0; + + /* get the attrs so we can delete it */ + rc = uefi_call_wrapper(RT->GetVariable, 5, name, guid, &attrs, NULL, NULL); + if (EFI_ERROR(rc)) { + if (rc == EFI_NOT_FOUND) { + fwup_debug(L"Not deleting variable '%s' as not found", name); + return EFI_SUCCESS; + } + fwup_debug(L"Could not get variable '%s' for delete: %r", name, rc); + return rc; + } + return uefi_call_wrapper(RT->SetVariable, 5, name, guid, attrs, 0, NULL); +} + +EFI_STATUS +fwup_set_variable(CHAR16 *name, EFI_GUID *guid, VOID *data, UINTN size, UINT32 attrs) +{ + return uefi_call_wrapper(RT->SetVariable, 5, name, guid, attrs, size, data); +} + +EFI_STATUS +fwup_get_variable(CHAR16 *name, EFI_GUID *guid, VOID **buf_out, UINTN *buf_size_out, UINT32 *attrs_out) +{ + EFI_STATUS rc; + UINTN size = 0; + UINT32 attrs; + _cleanup_free VOID *buf = NULL; + + rc = uefi_call_wrapper(RT->GetVariable, 5, name, guid, &attrs, &size, NULL); + if (EFI_ERROR(rc)) { + if (rc == EFI_BUFFER_TOO_SMALL) { + buf = fwup_malloc(size); + if (buf == NULL) + return EFI_OUT_OF_RESOURCES; + } else if (rc != EFI_NOT_FOUND) { + fwup_debug(L"Could not get variable '%s': %r", name, rc); + return rc; + } + } else { + fwup_debug(L"GetVariable(%s) succeeded with size=0", name); + return EFI_INVALID_PARAMETER; + } + rc = uefi_call_wrapper(RT->GetVariable, 5, name, guid, &attrs, &size, buf); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not get variable '%s': %r", name, rc); + return rc; + } + *buf_out = _steal_pointer(&buf); + *buf_size_out = size; + *attrs_out = attrs; + return EFI_SUCCESS; +} diff --git a/plugins/uefi/efi/fwup-efi.h b/plugins/uefi/efi/fwup-efi.h index 5d55a85b1..bb2a8092e 100644 --- a/plugins/uefi/efi/fwup-efi.h +++ b/plugins/uefi/efi/fwup-efi.h @@ -12,40 +12,61 @@ #define UPDATE_INFO_VERSION 7 -#ifdef _EFI_INCLUDE_ -#define efidp_header EFI_DEVICE_PATH -#define efi_guid_t EFI_GUID -#endif /* _EFI_INCLUDE_ */ +static __attribute__((__unused__)) EFI_GUID empty_guid = + {0x0,0x0,0x0,{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}}; +static __attribute__((__unused__))EFI_GUID fwupdate_guid = + {0x0abba7dc,0xe516,0x4167,{0xbb,0xf5,0x4d,0x9d,0x1c,0x73,0x94,0x16}}; +static __attribute__((__unused__))EFI_GUID ux_capsule_guid = + {0x3b8c8162,0x188c,0x46a4,{0xae,0xc9,0xbe,0x43,0xf1,0xd6,0x56,0x97}}; +static __attribute__((__unused__))EFI_GUID global_variable_guid = EFI_GLOBAL_VARIABLE; typedef struct { - uint8_t version; - uint8_t checksum; - uint8_t image_type; - uint8_t reserved; - uint32_t mode; - uint32_t x_offset; - uint32_t y_offset; -} ux_capsule_header_t; + UINT8 version; + UINT8 checksum; + UINT8 image_type; + UINT8 reserved; + UINT32 mode; + UINT32 x_offset; + UINT32 y_offset; +} __attribute__((__packed__)) UX_CAPSULE_HEADER; -typedef struct update_info_s { - uint32_t update_info_version; +typedef struct { + UINT32 update_info_version; /* stuff we need to apply an update */ - efi_guid_t guid; - uint32_t capsule_flags; - uint64_t hw_inst; + EFI_GUID guid; + UINT32 capsule_flags; + UINT64 hw_inst; - EFI_TIME time_attempted; + EFI_TIME time_attempted; /* our metadata */ - uint32_t status; + UINT32 status; /* variadic device path */ union { - efidp_header *dp_ptr; - efidp_header dp; - uint8_t dp_buf[0]; + EFI_DEVICE_PATH dp; + UINT8 dp_buf[0]; }; -} __attribute__((__packed__)) update_info; +} __attribute__((__packed__)) FWUP_UPDATE_INFO; + +typedef struct { + UINT32 attributes; + UINT16 file_path_list_length; + CHAR16 *description; +} __attribute__((__packed__)) EFI_LOAD_OPTION; + +EFI_STATUS fwup_delete_variable (CHAR16 *name, + EFI_GUID *guid); +EFI_STATUS fwup_set_variable (CHAR16 *name, + EFI_GUID *guid, + VOID *data, + UINTN size, + UINT32 attrs); +EFI_STATUS fwup_get_variable (CHAR16 *name, + EFI_GUID *guid, + VOID **buf_out, + UINTN *buf_size_out, + UINT32 *attrs_out); #endif /* _FWUP_EFI_H */ diff --git a/plugins/uefi/efi/fwupdate.c b/plugins/uefi/efi/fwupdate.c index 3cbe16f6d..5dd32729c 100644 --- a/plugins/uefi/efi/fwupdate.c +++ b/plugins/uefi/efi/fwupdate.c @@ -7,285 +7,37 @@ #include #include -#include - +#include "fwup-cleanups.h" +#include "fwup-common.h" #include "fwup-efi.h" -#include "hexdump.h" +#include "fwup-debug.h" #define UNUSED __attribute__((__unused__)) +#define GNVN_BUF_SIZE 1024 +#define FWUP_NUM_CAPSULE_UPDATES_MAX 128 -EFI_GUID empty_guid = {0x0,0x0,0x0,{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}}; -EFI_GUID fwupdate_guid = - {0x0abba7dc,0xe516,0x4167,{0xbb,0xf5,0x4d,0x9d,0x1c,0x73,0x94,0x16}}; -EFI_GUID ux_capsule_guid = - {0x3b8c8162,0x188c,0x46a4,{0xae,0xc9,0xbe,0x43,0xf1,0xd6,0x56,0x97}}; -EFI_GUID fmp_capsule_guid = - {0x6dcbd5ed,0xe82d,0x4c44,{0xbd,0xa1,0x71,0x94,0x19,0x9a,0xd9,0x2a}}; -EFI_GUID global_variable_guid = EFI_GLOBAL_VARIABLE; +typedef struct { + CHAR16 *name; + UINT32 attrs; + UINTN size; + FWUP_UPDATE_INFO *info; +} FWUP_UPDATE_TABLE; +static VOID +fwup_update_table_free(FWUP_UPDATE_TABLE *update) +{ + FreePool(update->info); + FreePool(update->name); + FreePool(update); +} -typedef struct update_table_s { - CHAR16 *name; - UINT32 attributes; - UINTN size; - update_info *info; -} update_table; - -static EFI_STATUS -delete_variable(CHAR16 *name, EFI_GUID guid, UINT32 attributes); -static EFI_STATUS -set_variable(CHAR16 *name, EFI_GUID guid, VOID *data, UINTN size, - UINT32 attrs); - -static int debugging; +_DEFINE_CLEANUP_FUNCTION0(FWUP_UPDATE_TABLE *, _fwup_update_table_free_p, fwup_update_table_free) +#define _cleanup_update_table __attribute__ ((cleanup(_fwup_update_table_free_p))) #define SECONDS 1000000 -static VOID -msleep(unsigned long msecs) -{ - BS->Stall(msecs); -} - - -/* - * I'm not actually sure when these appear, but they're present in the - * version in front of me. - */ -#if defined(__GNUC__) && defined(__GNUC_MINOR__) -#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 -#define uintn_mult(a, b, c) __builtin_mul_overflow(a, b, c) -#endif -#endif -#ifndef uintn_mult -#define uintn_mult(a, b, c) ({ \ - const UINTN _limit = ~0UL; \ - int _ret = 1; \ - if ((a) != 0 && (b) != 0) { \ - _ret = _limit / (a) < (b); \ - } \ - if (!_ret) \ - *(c) = ((a) * (b)); \ - _ret; \ - }) -#endif - -int -debug_print(const char *func, const char *file, const int line, - CHAR16 *fmt, ...) -{ - va_list args0, args1; - CHAR16 *out0, *out1; - UINT32 attrs = EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS; - CHAR16 *name = L"FWUPDATE_DEBUG_LOG"; - static bool once = true; - - va_start(args0, fmt); - out0 = VPoolPrint(fmt, args0); - va_end(args0); - if (!out0) { - if (debugging) { - va_start(args1, fmt); - VPrint(fmt, args1); - va_end(args1); - msleep(200000); - } - Print(L"fwupdate: Allocation for debug log failed!\n"); - return debugging; - } - if (debugging) - Print(L"%s", out0); - out1 = PoolPrint(L"%a:%d:%a(): %s", file, line, func, out0); - FreePool(out0); - if (!out1) { - Print(L"fwupdate: Allocation for debug log failed!\n"); - return debugging; - } - - if (once) { - once = false; - delete_variable(name, fwupdate_guid, attrs); - } else { - attrs |= EFI_VARIABLE_APPEND_WRITE; - } - set_variable(name, fwupdate_guid, out1, StrSize(out1) - sizeof (CHAR16), attrs); - - FreePool(out1); - - return debugging; -} - -#define dprint(fmt, args...) debug_print(__func__, __FILE__, __LINE__, fmt, ## args ) -#define print(fmt, args...) ({ if (!dprint(fmt, ## args)) Print(fmt, ## args); }) - -/* - * Allocate some raw pages that aren't part of the pool allocator. - */ -static EFI_STATUS -allocate(void **addr, UINTN size) -{ - /* - * We're actually guaranteed that page size is 4096 by UEFI. - */ - UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0); - EFI_STATUS rc; - EFI_PHYSICAL_ADDRESS pageaddr = 0; - EFI_ALLOCATE_TYPE type = AllocateAnyPages; - - if (sizeof (VOID *) == 4) { - pageaddr = 0xffffffffULL - 8192; - type = AllocateMaxAddress; - } - - rc = uefi_call_wrapper(BS->AllocatePages, 4, type, - EfiLoaderData, pages, - &pageaddr); - if (EFI_ERROR(rc)) - return rc; - if (sizeof (VOID *) == 4 && pageaddr > 0xffffffffULL) { - uefi_call_wrapper(BS->FreePages, 2, pageaddr, pages); - print(L"Got bad allocation at 0x%016x\n", (UINT64)pageaddr); - return EFI_OUT_OF_RESOURCES; - } - *addr = (void *)(UINTN)pageaddr; - return rc; -} - -/* - * Free our raw page allocations. - */ -static EFI_STATUS -free(void *addr, UINTN size) -{ - UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0); - EFI_STATUS rc; - - rc = uefi_call_wrapper(BS->FreePages, 2, - (EFI_PHYSICAL_ADDRESS)(UINTN)addr, - pages); - return rc; -} - -static inline int -guid_cmp(efi_guid_t *a, efi_guid_t *b) -{ - return CompareMem(a, b, sizeof (*a)); -} - -EFI_STATUS -read_file(EFI_FILE_HANDLE fh, UINT8 **buf_out, UINTN *buf_size_out) -{ - UINT8 *b = NULL; - const UINTN bs = 512; - UINTN n_blocks = 4096; - UINTN i = 0; - EFI_STATUS rc; - - while (1) { - void *newb = NULL; - UINTN news = 0; - if (uintn_mult(bs * 2, n_blocks, &news)) { - if (b) - free(b, bs * n_blocks); - print(L"allocation %d * %d would overflow size\n", - bs * 2, n_blocks); - return EFI_OUT_OF_RESOURCES; - } - rc = allocate(&newb, news); - if (EFI_ERROR(rc)) { - print(L"Tried to allocate %d\n", - bs * n_blocks * 2); - print(L"Could not allocate memory.\n"); - return EFI_OUT_OF_RESOURCES; - } - if (b) { - CopyMem(newb, b, bs * n_blocks); - free(b, bs * n_blocks); - } - b = newb; - n_blocks *= 2; - - for (; i < n_blocks; i++) { - EFI_STATUS rc; - UINTN sz = bs; - - rc = uefi_call_wrapper(fh->Read, 3, fh, &sz, - &b[i * bs]); - if (EFI_ERROR(rc)) { - free(b, bs * n_blocks); - print(L"Could not read file: %r\n", rc); - return rc; - } - - if (sz != bs) { - *buf_size_out = bs * i + sz; - *buf_out = b; - return EFI_SUCCESS; - } - } - } - return EFI_SUCCESS; -} - -static EFI_STATUS -delete_variable(CHAR16 *name, EFI_GUID guid, UINT32 attributes) -{ - return uefi_call_wrapper(RT->SetVariable, 5, name, &guid, attributes, - 0, NULL); -} - -static EFI_STATUS -set_variable(CHAR16 *name, EFI_GUID guid, VOID *data, UINTN size, - UINT32 attrs) -{ - return uefi_call_wrapper(RT->SetVariable, 5, name, &guid, attrs, - size, data); -} - -static EFI_STATUS -read_variable(CHAR16 *name, EFI_GUID guid, void **buf_out, UINTN *buf_size_out, - UINT32 *attributes_out) -{ - EFI_STATUS rc; - UINT32 attributes; - UINTN size = 0; - void *buf = NULL; - - rc = uefi_call_wrapper(RT->GetVariable, 5, name, - &guid, &attributes, &size, NULL); - if (EFI_ERROR(rc)) { - if (rc == EFI_BUFFER_TOO_SMALL) { - buf = AllocatePool(size); - if (!buf) { - print(L"Tried to allocate %d\n", size); - print(L"Could not allocate memory.\n"); - return EFI_OUT_OF_RESOURCES; - } - } else if (rc != EFI_NOT_FOUND) { - print(L"Could not get variable \"%s\": %r\n", name, rc); - return rc; - } - } else { - print(L"GetVariable(%s) succeeded with size=0.\n", name); - return EFI_INVALID_PARAMETER; - } - rc = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, &attributes, - &size, buf); - if (EFI_ERROR(rc)) { - print(L"Could not get variable \"%s\": %r\n", name, rc); - FreePool(buf); - return rc; - } - *buf_out = buf; - *buf_size_out = size; - *attributes_out = attributes; - return EFI_SUCCESS; -} - static INTN -dp_size(EFI_DEVICE_PATH *dp, INTN limit) +fwup_dp_size(EFI_DEVICE_PATH *dp, INTN limit) { INTN ret = 0; while (1) { @@ -305,288 +57,154 @@ dp_size(EFI_DEVICE_PATH *dp, INTN limit) } static EFI_STATUS -get_info(CHAR16 *name, update_table *info_out) +fwup_populate_update_info(CHAR16 *name, FWUP_UPDATE_TABLE *info_out) { EFI_STATUS rc; - update_info *info = NULL; + FWUP_UPDATE_INFO *info = NULL; UINTN info_size = 0; - UINT32 attributes = 0; - void *info_ptr = NULL; + UINT32 attrs = 0; + VOID *info_ptr = NULL; - rc = read_variable(name, fwupdate_guid, &info_ptr, &info_size, - &attributes); + rc = fwup_get_variable(name, &fwupdate_guid, &info_ptr, &info_size, &attrs); if (EFI_ERROR(rc)) return rc; - info = (update_info *)info_ptr; + info = (FWUP_UPDATE_INFO *)info_ptr; - if (info_size < sizeof (*info)) { - print(L"Update \"%s\" is is too small.\n", name); - delete_variable(name, fwupdate_guid, attributes); + if (info_size < sizeof(*info)) { + fwup_warning(L"Update '%s' is is too small", name); return EFI_INVALID_PARAMETER; } - if (info_size - sizeof (EFI_DEVICE_PATH) <= sizeof (*info)) { - print(L"Update \"%s\" is malformed, " - L"and cannot hold a file path.\n", name); - delete_variable(name, fwupdate_guid, attributes); + if (info_size - sizeof(EFI_DEVICE_PATH) <= sizeof(*info)) { + fwup_warning(L"Update '%s' is malformed, " + L"and cannot hold a file path", name); return EFI_INVALID_PARAMETER; } EFI_DEVICE_PATH *hdr = (EFI_DEVICE_PATH *)&info->dp; - INTN is = EFI_FIELD_OFFSET(update_info, dp); + INTN is = EFI_FIELD_OFFSET(FWUP_UPDATE_INFO, dp); if (is > (INTN)info_size) { - print(L"Update \"%s\" has an invalid file path.\n" - L"Device path offset is %d, but total size is %d\n", - name, is, info_size); - delete_variable(name, fwupdate_guid, attributes); + fwup_warning(L"Update '%s' has an invalid file path, " + L"device path offset is %d, but total size is %d", + name, is, info_size); return EFI_INVALID_PARAMETER; } is = info_size - is; - INTN sz = dp_size(hdr, info_size); - if (sz < 0 || is < 0) { -invalid_size: - print(L"Update \"%s\" has an invalid file path.\n" - L"update info size: %d dp size: %d size for dp: %d\n", - name, info_size, sz, is); - delete_variable(name, fwupdate_guid, attributes); + INTN sz = fwup_dp_size(hdr, info_size); + if (sz < 0 || is < 0 || is > (INTN)info_size || is != sz) { + fwup_warning(L"Update '%s' has an invalid file path, " + L"update info size: %d dp size: %d size for dp: %d", + name, info_size, sz, is); return EFI_INVALID_PARAMETER; } - if (is > (INTN)info_size) - goto invalid_size; - if (is != sz) - goto invalid_size; info_out->info = info; info_out->size = info_size; - info_out->attributes = attributes; + info_out->attrs = attrs; + info_out->name = StrDuplicate(name); + if (info_out->name == NULL) { + fwup_warning(L"Could not allocate %d", StrSize(name)); + return EFI_OUT_OF_RESOURCES; + } return EFI_SUCCESS; } static EFI_STATUS -find_updates(UINTN *n_updates_out, update_table ***updates_out) +fwup_populate_update_table(FWUP_UPDATE_TABLE **updates, UINTN *n_updates_out) { - EFI_STATUS rc; - update_table **updates = NULL; - UINTN n_updates = 0; - UINTN n_updates_allocated = 128; - EFI_STATUS ret = EFI_OUT_OF_RESOURCES; - -#define GNVN_BUF_SIZE 1024 - UINTN variable_name_allocation = GNVN_BUF_SIZE; - UINTN variable_name_size = 0; - CHAR16 *variable_name; EFI_GUID vendor_guid = empty_guid; - UINTN mult_res; - - if (uintn_mult(sizeof (update_table *), n_updates_allocated, - &mult_res)) { - print(L"Allocation %d * %d would overflow size\n", - sizeof (update_table *), n_updates_allocated); - return EFI_OUT_OF_RESOURCES; - } - - updates = AllocateZeroPool(mult_res); - if (!updates) { - print(L"Tried to allocate %d\n", mult_res); - print(L"Could not allocate memory.\n"); - return EFI_OUT_OF_RESOURCES; - } + EFI_STATUS rc; + UINTN n_updates = 0; + _cleanup_free CHAR16 *variable_name = NULL; /* How much do we trust "size of the VariableName buffer" to mean * sizeof(vn) and not sizeof(vn)/sizeof(vn[0]) ? */ - variable_name = AllocateZeroPool(GNVN_BUF_SIZE * 2); - if (!variable_name) { - print(L"Tried to allocate %d\n", GNVN_BUF_SIZE * 2); - print(L"Could not allocate memory.\n"); - FreePool(updates); + variable_name = fwup_malloc0(GNVN_BUF_SIZE * 2); + if (variable_name == NULL) return EFI_OUT_OF_RESOURCES; - } while (1) { - variable_name_size = variable_name_allocation; + UINTN variable_name_size = GNVN_BUF_SIZE; rc = uefi_call_wrapper(RT->GetNextVariableName, 3, &variable_name_size, variable_name, &vendor_guid); - if (rc == EFI_BUFFER_TOO_SMALL) { - /* If we don't have a big enough buffer to hold the - * name, allocate a bigger one and try again */ - UINTN new_allocation; - CHAR16 *new_name; - - new_allocation = variable_name_size; - if (uintn_mult(new_allocation, 2, &mult_res)) { - print(L"%d * 2 would overflow size\n", - new_allocation); - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - new_name = AllocatePool(new_allocation * 2); - if (!new_name) { - print(L"Tried to allocate %d\n", - new_allocation * 2); - print(L"Could not allocate memory.\n"); - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - CopyMem(new_name, variable_name, - variable_name_allocation); - variable_name_allocation = new_allocation; - FreePool(variable_name); - variable_name = new_name; - continue; - } else if (rc == EFI_NOT_FOUND) { + if (rc == EFI_NOT_FOUND) break; - } else if (EFI_ERROR(rc)) { - print(L"Could not get variable name: %r\n", rc); - ret = rc; - goto err; + + /* ignore any huge names */ + if (rc == EFI_BUFFER_TOO_SMALL) + continue; + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not get variable name: %r", rc); + return rc; } - /* - * If it's not one of our state variables, keep going. - */ - if (guid_cmp(&vendor_guid, &fwupdate_guid)) + /* not one of our state variables */ + if (CompareGuid(&vendor_guid, &fwupdate_guid)) continue; - /* - * Don't delete our debugging settings. - */ + /* ignore debugging settings */ if (StrCmp(variable_name, L"FWUPDATE_VERBOSE") == 0 || StrCmp(variable_name, L"FWUPDATE_DEBUG_LOG") == 0) continue; - UINTN vns = StrLen(variable_name); - CHAR16 vn[vns + 1]; - CopyMem(vn, variable_name, vns * sizeof (vn[0])); - vn[vns] = L'\0'; - print(L"Found update %s\n", vn); - - if (n_updates == n_updates_allocated) { - update_table **new_ups; - UINTN mul_a, mul_b; - if (uintn_mult(n_updates_allocated, 2, &mult_res)) { - mul_a = n_updates_allocated; - mul_b = 2; -mult_err: - print(L"Allocation %d * %d would overflow size\n", - mul_a, mul_b); - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - if (uintn_mult(mult_res, sizeof (update_table *), - &mult_res)) { - mul_a = mult_res; - mul_b = sizeof (update_table *); - goto mult_err; - } - - new_ups = AllocateZeroPool(mult_res); - if (!new_ups) { - print(L"Tried to allocate %d\n", mult_res); - print(L"Could not allocate memory.\n"); - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - CopyMem(new_ups, updates, mult_res); - n_updates_allocated *= 2; - FreePool(updates); - updates = new_ups; + if (n_updates > FWUP_NUM_CAPSULE_UPDATES_MAX) { + fwup_warning(L"Ignoring update %s", variable_name); + continue; } - update_table *update = AllocatePool(sizeof (update_table)); - if (!update) { - print(L"Tried to allocate %d\n", sizeof (update_table)); - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - - update->name = StrDuplicate(vn); - if (!update->name) { - print(L"Tried to allocate %d\n", StrSize(vn)); - ret = EFI_OUT_OF_RESOURCES; - FreePool(update); - goto err; - } - - rc = get_info(vn, update); + fwup_info(L"Found update %s", variable_name); + _cleanup_update_table FWUP_UPDATE_TABLE *update = fwup_malloc0(sizeof(FWUP_UPDATE_TABLE)); + if (update == NULL) + return EFI_OUT_OF_RESOURCES; + rc = fwup_populate_update_info(variable_name, update); if (EFI_ERROR(rc)) { - print(L"Could not get update info for \"%s\", aborting.\n", vn); - ret = rc; - FreePool(update->name); - FreePool(update); - goto err; + fwup_delete_variable(variable_name, &fwupdate_guid); + fwup_warning(L"Could not populate update info for '%s'", variable_name); + return rc; } if (update->info->status & FWUPDATE_ATTEMPT_UPDATE) { - EFI_TIME_CAPABILITIES timecaps = { 0, }; - - uefi_call_wrapper(RT->GetTime, 2, - &update->info->time_attempted, - &timecaps); + fwup_time(&update->info->time_attempted); update->info->status = FWUPDATE_ATTEMPTED; - updates[n_updates++] = update; - } else { - FreePool(update->info); - FreePool(update->name); - FreePool(update); + updates[n_updates++] = _steal_pointer(&update); } } - FreePool(variable_name); - *n_updates_out = n_updates; - *updates_out = updates; - return EFI_SUCCESS; -err: - FreePool(variable_name); - - for (unsigned int i = 0; i < n_updates; i++) { - FreePool(updates[i]->name); - FreePool(updates[i]->info); - FreePool(updates[i]); - } - - FreePool(updates); - return ret; } static EFI_STATUS -search_file(EFI_DEVICE_PATH **file_dp, EFI_FILE_HANDLE *fh) +fwup_search_file(EFI_DEVICE_PATH **file_dp, EFI_FILE_HANDLE *fh) { EFI_DEVICE_PATH *dp, *parent_dp; EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_GUID dpp = DEVICE_PATH_PROTOCOL; - EFI_FILE_HANDLE *devices; - UINTN i, n_handles, count; + UINTN n_handles, count; EFI_STATUS rc; + _cleanup_free EFI_FILE_HANDLE *devices = NULL; rc = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &sfsp, NULL, &n_handles, (EFI_HANDLE **)&devices); if (EFI_ERROR(rc)) { - print(L"Could not find handles.\n"); + fwup_warning(L"Could not find handles"); return rc; } dp = *file_dp; - if (debugging) - print(L"Searching Device Path: %s ...\n", DevicePathToStr(dp)); - + fwup_debug(L"Searching Device Path: %s...", DevicePathToStr(dp)); parent_dp = DuplicateDevicePath(dp); - if (!parent_dp) { - rc = EFI_INVALID_PARAMETER; - goto out; - } + if (parent_dp == NULL) + return EFI_INVALID_PARAMETER; dp = parent_dp; count = 0; while (1) { - if (IsDevicePathEnd(dp)) { - rc = EFI_INVALID_PARAMETER; - goto out; - } + if (IsDevicePathEnd(dp)) + return EFI_INVALID_PARAMETER; if (DevicePathType(dp) == MEDIA_DEVICE_PATH && DevicePathSubType(dp) == MEDIA_FILEPATH_DP) @@ -597,58 +215,45 @@ search_file(EFI_DEVICE_PATH **file_dp, EFI_FILE_HANDLE *fh) } SetDevicePathEndNode(dp); + fwup_debug(L"Device Path prepared: %s", DevicePathToStr(parent_dp)); - if (debugging) - print(L"Device Path prepared: %s\n", - DevicePathToStr(parent_dp)); - - for (i = 0; i < n_handles; i++) { + for (UINTN i = 0; i < n_handles; i++) { EFI_DEVICE_PATH *path; rc = uefi_call_wrapper(BS->HandleProtocol, 3, devices[i], &dpp, - (void **)&path); + (VOID **)&path); if (EFI_ERROR(rc)) continue; - if (debugging) - print(L"Device supporting SFSP: %s\n", - DevicePathToStr(path)); + fwup_debug(L"Device supporting SFSP: %s", DevicePathToStr(path)); - rc = EFI_UNSUPPORTED; while (!IsDevicePathEnd(path)) { - if (debugging) - print(L"Comparing: %s and %s\n", - DevicePathToStr(parent_dp), - DevicePathToStr(path)); + fwup_debug(L"Comparing: %s and %s", + DevicePathToStr(parent_dp), + DevicePathToStr(path)); if (LibMatchDevicePaths(path, parent_dp) == TRUE) { *fh = devices[i]; - for (i = 0; i < count; i++) + for (UINTN j = 0; j < count; j++) *file_dp = NextDevicePathNode(*file_dp); - rc = EFI_SUCCESS; - if (debugging) - print(L"Match up! Returning %s\n", - DevicePathToStr(*file_dp)); - - goto out; + fwup_debug(L"Match up! Returning %s", + DevicePathToStr(*file_dp)); + return EFI_SUCCESS; } path = NextDevicePathNode(path); } } -out: - if (!EFI_ERROR(rc)) - print(L"File %s searched\n", DevicePathToStr(*file_dp)); - - uefi_call_wrapper(BS->FreePool, 1, devices); - return rc; + fwup_warning(L"Failed to find '%s' DevicePath", DevicePathToStr(*file_dp)); + return EFI_UNSUPPORTED; } static EFI_STATUS -open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh) +fwup_open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh) { + CONST UINTN devpath_max_size = 1024; /* arbitrary limit */ EFI_DEVICE_PATH *file_dp = dp; EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_FILE_HANDLE device; @@ -659,16 +264,16 @@ open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh) rc = uefi_call_wrapper(BS->LocateDevicePath, 3, &sfsp, &file_dp, (EFI_HANDLE *)&device); if (EFI_ERROR(rc)) { - rc = search_file(&file_dp, &device); + rc = fwup_search_file(&file_dp, &device); if (EFI_ERROR(rc)) { - print(L"Could not locate device handle: %r\n", rc); + fwup_warning(L"Could not locate device handle: %r", rc); return rc; } } if (DevicePathType(file_dp) != MEDIA_DEVICE_PATH || - DevicePathSubType(file_dp) != MEDIA_FILEPATH_DP) { - print(L"Could not find appropriate device.\n"); + DevicePathSubType(file_dp) != MEDIA_FILEPATH_DP) { + fwup_warning(L"Could not find appropriate device"); return EFI_UNSUPPORTED; } @@ -677,81 +282,65 @@ open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh) CopyMem(&sz16, &file_dp->Length[0], sizeof(sz16)); sz = sz16; sz -= 4; - if (sz <= 6 || sz % 2 != 0) { - print(L"Invalid file device path.\n"); + if (sz <= 6 || sz % 2 != 0 || + sz > devpath_max_size * sizeof(CHAR16)) { + fwup_warning(L"Invalid file device path of size %d", sz); return EFI_INVALID_PARAMETER; } - sz /= sizeof (CHAR16); - /* - * check against some arbitrary limit to avoid having a stack - * overflow here. - */ - if (sz > 1024) { - print(L"Invalid file device path.\n"); - return EFI_INVALID_PARAMETER; - } - CHAR16 filename[sz+1]; - CopyMem(filename, (UINT8 *)file_dp + 4, sz * sizeof (CHAR16)); - filename[sz] = L'\0'; + _cleanup_free CHAR16 *filename = fwup_malloc0(sz + sizeof(CHAR16)); + CopyMem(filename, (UINT8 *)file_dp + 4, sz); rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, &sfsp, - (void **)&drive); + (VOID **)&drive); if (EFI_ERROR(rc)) { - print(L"Could not open device interface: %r.\n", rc); + fwup_warning(L"Could not open device interface: %r", rc); return rc; } - dprint(L"Found device\n"); + fwup_debug(L"Found device"); rc = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); if (EFI_ERROR(rc)) { - print(L"Could not open volume: %r.\n", rc); + fwup_warning(L"Could not open volume: %r", rc); return rc; } - dprint(L"Found volume\n"); + fwup_debug(L"Found volume"); rc = uefi_call_wrapper(root->Open, 5, root, fh, filename, EFI_FILE_MODE_READ, 0); if (EFI_ERROR(rc)) { - print(L"Could not open file \"%s\": %r.\n", filename, rc); + fwup_warning(L"Could not open file '%s': %r", filename, rc); return rc; } - dprint(L"Found file\n"); + fwup_debug(L"Found file"); return EFI_SUCCESS; } static EFI_STATUS -delete_boot_order(CHAR16 *name, EFI_GUID guid) +fwup_delete_boot_order(CHAR16 *name, EFI_GUID guid) { - - UINTN i; UINT16 boot_num; EFI_STATUS rc; UINTN info_size = 0; - UINT32 attributes = 0; - void *info_ptr = NULL; - UINT16 *new_info_ptr = NULL; - BOOLEAN num_found = FALSE; + UINT32 attrs = 0; + _cleanup_free VOID *info_ptr = NULL; + _cleanup_free UINT16 *new_info_ptr = NULL; + UINT8 num_found = FALSE; UINTN new_list_num = 0; /* get boot hex number */ boot_num = xtoi((CHAR16 *)((UINT8 *)name + sizeof(L"Boot"))); - rc = read_variable(L"BootOrder", guid, &info_ptr, &info_size, - &attributes); + rc = fwup_get_variable(L"BootOrder", &guid, &info_ptr, &info_size, &attrs); if (EFI_ERROR(rc)) return rc; - new_info_ptr = AllocatePool(info_size); - if (!new_info_ptr) { - print(L"Tried to allocate %d\n", info_size); - print(L"Could not allocate memory.\n"); - FreePool(info_ptr); + new_info_ptr = fwup_malloc(info_size); + if (new_info_ptr == NULL) return EFI_OUT_OF_RESOURCES; - } - for (i = 0; i < (info_size / sizeof(UINT16)) ; i++) { + for (UINTN i = 0; i < (info_size / sizeof(UINT16)) ; i++) { if (((UINT16 *)info_ptr)[i] != boot_num) { new_info_ptr[i] = ((UINT16 *)info_ptr)[i]; new_list_num++; @@ -762,166 +351,121 @@ delete_boot_order(CHAR16 *name, EFI_GUID guid) } /* if not in the BootOrder list, do not update BootOrder */ - if (!num_found) { - rc = EFI_SUCCESS; - goto out; - } + if (!num_found) + return EFI_SUCCESS; rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &guid, - attributes, new_list_num * sizeof(UINT16), + attrs, new_list_num * sizeof(UINT16), new_info_ptr); if (EFI_ERROR(rc)) { - print(L"Could not update variable status for \"%s\": %r\n", - name, rc); - goto out; + fwup_warning(L"Could not update variable status for '%s': %r", + name, rc); + return rc; } - -out: - - FreePool(info_ptr); - FreePool(new_info_ptr); - return rc; } -static EFI_STATUS -delete_boot_entry(void) +/* TODO: move to gnu-efi: https://github.com/vathpela/gnu-efi/issues/7 */ +static BOOLEAN +_StrHasPrefix(IN CONST CHAR16 *s1, IN CONST CHAR16 *s2) { - EFI_STATUS rc; - - UINTN variable_name_allocation = GNVN_BUF_SIZE; - UINTN variable_name_size = 0; - CHAR16 *variable_name; - EFI_GUID vendor_guid = empty_guid; - UINTN mult_res; - EFI_STATUS ret = EFI_OUT_OF_RESOURCES; - - variable_name = AllocateZeroPool(GNVN_BUF_SIZE * 2); - if (!variable_name) { - print(L"Tried to allocate %d\n", GNVN_BUF_SIZE * 2); - print(L"Could not allocate memory.\n"); - return EFI_OUT_OF_RESOURCES; + while (*s2) { + if (*s1 == L'\0' || *s1 != *s2) + return FALSE; + s1 += 1; + s2 += 1; } - - while (1) { - variable_name_size = variable_name_allocation; - rc = uefi_call_wrapper(RT->GetNextVariableName, 3, - &variable_name_size, variable_name, - &vendor_guid); - if (rc == EFI_BUFFER_TOO_SMALL) { - - UINTN new_allocation; - CHAR16 *new_name; - - new_allocation = variable_name_size; - if (uintn_mult(new_allocation, 2, &mult_res)) { - print(L"%d * 2 would overflow size\n", - new_allocation); - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - new_name = AllocatePool(new_allocation * 2); - if (!new_name) { - print(L"Tried to allocate %d\n", - new_allocation * 2); - print(L"Could not allocate memory.\n"); - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - CopyMem(new_name, variable_name, - variable_name_allocation); - variable_name_allocation = new_allocation; - FreePool(variable_name); - variable_name = new_name; - continue; - } else if (rc == EFI_NOT_FOUND) { - break; - } else if (EFI_ERROR(rc)) { - print(L"Could not get variable name: %r\n", rc); - ret = rc; - goto err; - } - - /* check if the variable name is Boot#### */ - UINTN vns = StrLen(variable_name); - if (!guid_cmp(&vendor_guid, &global_variable_guid) - && vns == 8 && CompareMem(variable_name, L"Boot", 8) == 0) { - UINTN info_size = 0; - UINT32 attributes = 0; - void *info_ptr = NULL; - CHAR16 *load_op_description = NULL; - CHAR16 target[] = L"Linux Firmware Updater"; - - rc = read_variable(variable_name, vendor_guid, - &info_ptr, &info_size, &attributes); - if (EFI_ERROR(rc)) { - ret = rc; - goto err; - } - - /* - * check if the boot path created by fwupdate, - * check with EFI_LOAD_OPTION decription - */ - load_op_description = (CHAR16 *) - ((UINT8 *)info_ptr - + sizeof(UINT32) - + sizeof(UINT16)); - - if (CompareMem(load_op_description, target, - sizeof(target) - 2) == 0) { - /* delete the boot path from BootOrder list */ - rc = delete_boot_order(variable_name, - vendor_guid); - if (EFI_ERROR(rc)) { - print(L"Failed to delete the Linux Firmware Updater boot entry from BootOrder.\n"); - FreePool(info_ptr); - ret = rc; - goto err; - } - - rc = delete_variable(variable_name, - vendor_guid, attributes); - if (EFI_ERROR(rc)) { - print(L"Failed to delete the Linux Firmware Updater boot entry.\n"); - FreePool(info_ptr); - ret = rc; - goto err; - } - - FreePool(info_ptr); - break; - } - - FreePool(info_ptr); - } - } - - ret = EFI_SUCCESS; -err: - FreePool(variable_name); - return ret; + return TRUE; } static EFI_STATUS -get_gop_mode(UINT32 *mode, EFI_HANDLE loaded_image) +fwup_delete_boot_entry(VOID) +{ + EFI_STATUS rc; + UINTN variable_name_size = 0; + _cleanup_free CHAR16 *variable_name = NULL; + EFI_GUID vendor_guid = empty_guid; + + variable_name = fwup_malloc0(GNVN_BUF_SIZE * 2); + if (variable_name == NULL) + return EFI_OUT_OF_RESOURCES; + + while (1) { + variable_name_size = GNVN_BUF_SIZE; + rc = uefi_call_wrapper(RT->GetNextVariableName, 3, + &variable_name_size, variable_name, + &vendor_guid); + if (rc == EFI_NOT_FOUND) + break; + /* ignore any huge names */ + if (rc == EFI_BUFFER_TOO_SMALL) + continue; + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not get variable name: %r", rc); + return rc; + } + + /* check if the variable name is Boot#### */ + if (CompareGuid(&vendor_guid, &global_variable_guid) != 0) + continue; + if (StrCmp(variable_name, L"Boot") != 0) + continue; + + UINTN info_size = 0; + _cleanup_free VOID *info_ptr = NULL; + + /* get the data */ + rc = fwup_get_variable(variable_name, &vendor_guid, + &info_ptr, &info_size, NULL); + if (EFI_ERROR(rc)) + return rc; + if (info_size < sizeof(EFI_LOAD_OPTION)) + continue; + + /* + * check if the boot path created by fwupdate, + * check with EFI_LOAD_OPTION decription + */ + EFI_LOAD_OPTION *load_op = (EFI_LOAD_OPTION *) info_ptr; + if (_StrHasPrefix(load_op->description, L"Linux Firmware Updater") || + _StrHasPrefix(load_op->description, L"Linux-Firmware-Updater")) { + /* delete the boot path from BootOrder list */ + rc = fwup_delete_boot_order(variable_name, vendor_guid); + if (EFI_ERROR(rc)) { + fwup_warning(L"Failed to delete boot entry from BootOrder"); + return rc; + } + rc = fwup_delete_variable(variable_name, &vendor_guid); + if (EFI_ERROR(rc)) { + fwup_warning(L"Failed to delete boot entry"); + return rc; + } + break; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS +fwup_get_gop_mode(UINT32 *mode, EFI_HANDLE loaded_image) { EFI_HANDLE *handles, gop_handle; - UINTN num_handles, i; + UINTN num_handles; EFI_STATUS status; EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; - void *iface; + VOID *iface; status = LibLocateHandle(ByProtocol, &gop_guid, NULL, &num_handles, &handles); if (EFI_ERROR(status)) return status; - if (!handles || num_handles == 0) + if (handles == NULL || num_handles == 0) return EFI_UNSUPPORTED; - for (i = 0; i < num_handles; i++) { + for (UINTN i = 0; i < num_handles; i++) { gop_handle = handles[i]; status = uefi_call_wrapper(BS->OpenProtocol, 6, @@ -940,88 +484,24 @@ get_gop_mode(UINT32 *mode, EFI_HANDLE loaded_image) return EFI_UNSUPPORTED; } -static UINT32 csum(UINT8 *buf, UINTN size) -{ - UINT32 sum = 0; - UINTN i; - - dprint(L"checksumming %d bytes at 0x%08x\n", size, buf); - - for (i = 0; i < size; i++) { - sum += buf[i]; - if (debugging) - Print(L"\rpos:%08lx csum:%d", buf+i, sum); - } - if (debugging) - Print(L"\n"); - - return sum; -} - static EFI_STATUS -do_ux_csum(EFI_HANDLE loaded_image, UINT8 *buf, UINTN size) +fwup_check_gop_for_ux_capsule(EFI_HANDLE loaded_image, + EFI_CAPSULE_HEADER *capsule) { - ux_capsule_header_t *payload_hdr; - EFI_CAPSULE_HEADER *capsule; + UX_CAPSULE_HEADER *payload_hdr; EFI_STATUS rc; - UINTN sum = 0; - UINT8 *current = buf; - UINTN left = size; - - if (size < sizeof(*capsule)) { - dprint(L"Invalid capsule size %d\n", size); - return EFI_INVALID_PARAMETER; - } - - capsule = (EFI_CAPSULE_HEADER *)buf; - - if (debugging) - hexdump(buf, size <= 0x40 ? size : 0x40); - - dprint(L"size: %d\n", size); - dprint(L"&HeaderSize: 0x%08lx\n", &capsule->HeaderSize); - dprint(L"HeaderSize: %d\n", capsule->HeaderSize); - dprint(L"&CapsuleImageSize: 0x%08lx\n", &capsule->CapsuleImageSize); - dprint(L"CapsuleImageSize: %d\n", capsule->CapsuleImageSize); - - if (size < capsule->HeaderSize) { - dprint(L"Invalid capsule header size %d\n", size); - return EFI_INVALID_PARAMETER; - } - - sum += csum(current, capsule->HeaderSize); - current += capsule->HeaderSize; - left -= capsule->HeaderSize; - - payload_hdr = (ux_capsule_header_t *)(buf) + capsule->HeaderSize; - dprint(L"&PayloadHeader: 0x%08lx\n", payload_hdr); - dprint(L"PayloadHeader Size: %d\n", sizeof (*payload_hdr)); - rc = get_gop_mode(&payload_hdr->mode, loaded_image); + payload_hdr = (UX_CAPSULE_HEADER *)((UINT8*) capsule) + capsule->HeaderSize; + rc = fwup_get_gop_mode(&payload_hdr->mode, loaded_image); if (EFI_ERROR(rc)) return EFI_UNSUPPORTED; - payload_hdr->checksum = 0; - sum += csum(current, sizeof(*payload_hdr)); - current += sizeof(*payload_hdr); - left -= sizeof(*payload_hdr); - - sum += csum(current, left); - dprint(L"sum is 0x%02hhx; setting ->checksum to 0x%02hhx\n", - sum & 0xff, (uint8_t)((int8_t)(0 - (sum & 0xff)))); - - payload_hdr->checksum = (uint8_t)((int8_t)(0 - sum)); - dprint(L"checksum is set...\n"); - return EFI_SUCCESS; } -#define is_ux_capsule(guid) (guid_cmp(guid, &ux_capsule_guid) == 0) -#define is_fmp_capsule(guid) (guid_cmp(guid, &fmp_capsule_guid) == 0) - static EFI_STATUS -add_capsule(update_table *update, EFI_CAPSULE_HEADER **capsule_out, - EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_out, EFI_HANDLE loaded_image) +fwup_add_update_capsule(FWUP_UPDATE_TABLE *update, EFI_CAPSULE_HEADER **capsule_out, + EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_out, EFI_HANDLE loaded_image) { EFI_STATUS rc; EFI_FILE_HANDLE fh = NULL; @@ -1033,87 +513,42 @@ add_capsule(update_table *update, EFI_CAPSULE_HEADER **capsule_out, EFI_PHYSICAL_ADDRESS cbd_data; EFI_CAPSULE_HEADER *cap_out; - rc = open_file((EFI_DEVICE_PATH *)update->info->dp_buf, &fh); + rc = fwup_open_file((EFI_DEVICE_PATH *)update->info->dp_buf, &fh); if (EFI_ERROR(rc)) return rc; - rc = read_file(fh, &fbuf, &fsize); + rc = fwup_read_file(fh, &fbuf, &fsize); if (EFI_ERROR(rc)) return rc; uefi_call_wrapper(fh->Close, 1, fh); - dprint(L"Read file; %d bytes\n", fsize); - dprint(L"updates guid: %g\n", &update->info->guid); - dprint(L"File guid: %g\n", fbuf); - - /* - * See if it has the capsule header, and if not, add one. - * - * Unfortunately there's not a good way to do this, so we're just - * checking if the capsule has the fw_class guid at the right place. - */ - if ((guid_cmp(&update->info->guid, (efi_guid_t *)fbuf) == 0 || - is_fmp_capsule((efi_guid_t *)fbuf)) && - /* - * We're ignoring things that are 40 bytes here, because that's - * the size of the variables used in the test code I wrote for - * edk2 - It's basically a capsule header with no payload, so - * there's nothing real it can do anyway. - * - * At some point I'll update that to be slightly different and - * take the exception out, but it's not pressing. - */ - fsize != 40) { - dprint(L"Image has capsule image embedded\n"); - cbd_len = fsize; - cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf; - capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf; - if (!cap_out->Flags && !is_ux_capsule(&update->info->guid)) { -#if defined(__aarch64__) - cap_out->Flags |= update->info->capsule_flags; -#else - cap_out->Flags |= update->info->capsule_flags | - CAPSULE_FLAGS_PERSIST_ACROSS_RESET | - CAPSULE_FLAGS_INITIATE_RESET; -#endif - } - } else { - dprint(L"Image does not have embedded header\n"); - dprint(L"Allocating %d for capsule header.\n", - sizeof (*capsule)+fsize); - rc = allocate((void **)&capsule, sizeof (*capsule) + fsize); - if (EFI_ERROR(rc)) { - print(L"Tried to allocate %d\n", - sizeof (*capsule) + fsize); - print(L"Could not allocate space for update: %r.\n", - rc); - return EFI_OUT_OF_RESOURCES; - } - capsule->CapsuleGuid = update->info->guid; - capsule->HeaderSize = sizeof (*capsule); - if (!is_ux_capsule(&update->info->guid)) { -#if defined(__aarch64__) - capsule->Flags |= update->info->capsule_flags; -#else - capsule->Flags = update->info->capsule_flags | - CAPSULE_FLAGS_PERSIST_ACROSS_RESET | - CAPSULE_FLAGS_INITIATE_RESET; -#endif - } - capsule->CapsuleImageSize = fsize + sizeof (*capsule); - - UINT8 *buffer = (UINT8 *)capsule + capsule->HeaderSize; - CopyMem(buffer, fbuf, fsize); - cbd_len = capsule->CapsuleImageSize; - cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)capsule; - cap_out = capsule; - free(fbuf, fsize); + if (fsize < sizeof(EFI_CAPSULE_HEADER)) { + fwup_warning(L"Invalid capsule size %d", fsize); + return EFI_INVALID_PARAMETER; } - if (is_ux_capsule(&update->info->guid)) { - dprint(L"Checksumming ux capsule\n"); - rc = do_ux_csum(loaded_image, (UINT8 *)capsule, cbd_len); + fwup_debug(L"Read file; %d bytes", fsize); + fwup_debug(L"updates guid: %g", &update->info->guid); + fwup_debug(L"File guid: %g", fbuf); + + cbd_len = fsize; + cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf; + capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf; + if (cap_out->Flags == 0 && + CompareGuid(&update->info->guid, &ux_capsule_guid) != 0) { +#if defined(__aarch64__) + cap_out->Flags |= update->info->capsule_flags; +#else + cap_out->Flags |= update->info->capsule_flags | + CAPSULE_FLAGS_PERSIST_ACROSS_RESET | + CAPSULE_FLAGS_INITIATE_RESET; +#endif + } + + if (CompareGuid(&update->info->guid, &ux_capsule_guid) == 0) { + fwup_debug(L"Checking GOP for ux capsule"); + rc = fwup_check_gop_for_ux_capsule(loaded_image, capsule); if (EFI_ERROR(rc)) return EFI_UNSUPPORTED; } @@ -1125,54 +560,49 @@ add_capsule(update_table *update, EFI_CAPSULE_HEADER **capsule_out, return EFI_SUCCESS; } - static EFI_STATUS -apply_capsules(EFI_CAPSULE_HEADER **capsules, - EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd, - UINTN num_updates, EFI_RESET_TYPE *reset) +fwup_apply_capsules(EFI_CAPSULE_HEADER **capsules, + EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd, + UINTN num_updates, EFI_RESET_TYPE *reset) { UINT64 max_capsule_size; EFI_STATUS rc; - rc = delete_boot_entry(); - if (EFI_ERROR(rc)) { - /* - * Print out deleting boot entry error, but still try to apply - * capsule. - */ - dprint(L"Could not delete boot entry: %r\n", - __FILE__, __func__, __LINE__, rc); - } + /* still try to apply capsule on failure */ + rc = fwup_delete_boot_entry(); + if (EFI_ERROR(rc)) + fwup_warning(L"Could not delete boot entry: %r", rc); rc = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, capsules, num_updates, &max_capsule_size, reset); - dprint(L"QueryCapsuleCapabilities: %r max: %ld reset:%d\n", - rc, max_capsule_size, *reset); - dprint(L"Capsules: %d\n", num_updates); + fwup_debug(L"QueryCapsuleCapabilities: %r max: %ld reset:%d", + rc, max_capsule_size, *reset); + fwup_debug(L"Capsules: %d", num_updates); - uefi_call_wrapper(BS->Stall, 1, 1 * SECONDS); + fwup_msleep(1 * SECONDS); rc = uefi_call_wrapper(RT->UpdateCapsule, 3, capsules, num_updates, (EFI_PHYSICAL_ADDRESS)(UINTN)cbd); if (EFI_ERROR(rc)) { - print(L"Could not apply capsule update: %r\n", rc); + fwup_warning(L"Could not apply capsule update: %r", rc); return rc; } return EFI_SUCCESS; } -static -EFI_STATUS -set_statuses(UINTN n_updates, update_table **updates) +static EFI_STATUS +fwup_set_update_statuses(FWUP_UPDATE_TABLE **updates) { EFI_STATUS rc; - for (UINTN i = 0; i < n_updates; i++) { - rc = uefi_call_wrapper(RT->SetVariable, 5, updates[i]->name, - &fwupdate_guid, updates[i]->attributes, - updates[i]->size, updates[i]->info); + for (UINTN i = 0; i < FWUP_NUM_CAPSULE_UPDATES_MAX; i++) { + if (updates[i]->name == NULL) + break; + rc = fwup_set_variable(updates[i]->name, &fwupdate_guid, + updates[i]->info, updates[i]->size, + updates[i]->attrs); if (EFI_ERROR(rc)) { - print(L"Coould not update variable status for \"%s\": %r\n", - updates[i]->name, rc); + fwup_warning(L"Could not update variable status for '%s': %r", + updates[i]->name, rc); return rc; } } @@ -1182,75 +612,56 @@ set_statuses(UINTN n_updates, update_table **updates) EFI_GUID SHIM_LOCK_GUID = {0x605dab50,0xe046,0x4300,{0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23}}; -static void +static VOID __attribute__((__optimize__("0"))) -debug_hook(void) +fwup_debug_hook(VOID) { EFI_GUID guid = SHIM_LOCK_GUID; UINTN data = 0; UINTN data_size = 1; EFI_STATUS efi_status; - UINT32 attributes; + UINT32 attrs; register volatile int x = 0; extern char _text UNUSED, _data UNUSED; - /* - * If SHIM_DEBUG is set, we're going to assume shim has done whatever - * is needed to get a debugger attached, and we just need to explain - * who and where we are, and also enable our debugging output. - */ + /* shim has done whatever is needed to get a debugger attached */ efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"SHIM_DEBUG", - &guid, &attributes, &data_size, &data); + &guid, &attrs, &data_size, &data); if (EFI_ERROR(efi_status) || data != 1) { efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"FWUPDATE_VERBOSE", - &fwupdate_guid, &attributes, + &fwupdate_guid, &attrs, &data_size, &data); - if (EFI_ERROR(efi_status) || data != 1) { + if (EFI_ERROR(efi_status) || data != 1) return; - } - debugging = 1; + fwup_debug_set_enabled(TRUE); return; } - debugging = 1; + fwup_debug_set_enabled(TRUE); if (x) return; x = 1; - print(L"add-symbol-file "DEBUGDIR - L"fwupdate.efi.debug %p -s .data %p\n", - &_text, &_data); + fwup_info(L"add-symbol-file "DEBUGDIR + L"fwupdate.efi.debug %p -s .data %p", + &_text, &_data); } EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) { EFI_STATUS rc; - update_table **updates = NULL; - UINTN n_updates = 0; + UINTN i, n_updates = 0; EFI_RESET_TYPE reset_type = EfiResetWarm; + _cleanup_free FWUP_UPDATE_TABLE **updates = NULL; InitializeLib(image, systab); - /* - * if SHIM_DEBUG is set, print info for our attached debugger. - */ - debug_hook(); + /* if SHIM_DEBUG is set, fwup_info info for our attached debugger */ + fwup_debug_hook(); - /* - * Basically the workflow here is: - * 1) find and validate any update state variables with the right GUID - * 2) allocate our capsule data structures and add the capsules - * #1 described - * 3) update status variables - * 4) apply the capsule updates - * 5) reboot - */ - - /* - * Step 1: find and validate update state variables - */ + /* step 1: find and validate update state variables */ /* XXX TODO: * 1) survey the reset types first, and separate into groups * according to them @@ -1258,78 +669,68 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) * so we can do multiple runs * 3) only select the ones from one type for the first go */ - rc = find_updates(&n_updates, &updates); + updates = fwup_new0(FWUP_UPDATE_TABLE *, FWUP_NUM_CAPSULE_UPDATES_MAX); + if (updates == NULL) + return EFI_OUT_OF_RESOURCES; + rc = fwup_populate_update_table(updates, &n_updates); if (EFI_ERROR(rc)) { - print(L"fwupdate: Could not find updates: %r\n", rc); + fwup_warning(L"Could not find updates: %r", rc); return rc; } if (n_updates == 0) { - print(L"fwupdate: No updates to process. Called in error?\n"); + fwup_warning(L"No updates to process. Called in error?"); return EFI_INVALID_PARAMETER; } - /* - * Step 2: Build our data structure and add the capsules to it. - */ - EFI_CAPSULE_HEADER *capsules[n_updates + 1]; + /* step 2: Build our data structure and add the capsules to it */ + _cleanup_free EFI_CAPSULE_HEADER **capsules = NULL; + capsules = fwup_new0(EFI_CAPSULE_HEADER *, n_updates + 1); EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_data; - UINTN i, j; - rc = allocate((void **)&cbd_data, - sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1)); - if (EFI_ERROR(rc)) { - print(L"Tried to allocate %d\n", - sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1)); - print(L"fwupdate: Could not allocate memory: %r.\n",rc); - return rc; - } - for (i = 0, j = 0; i < n_updates; i++) { - dprint(L"Adding new capsule\n"); - rc = add_capsule(updates[i], &capsules[j], &cbd_data[j], - image); + UINTN j = 0; + cbd_data = fwup_malloc_raw(sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1)); + if (cbd_data == NULL) + return EFI_OUT_OF_RESOURCES; + for (i = 0; i < n_updates; i++) { + fwup_info(L"Adding new capsule"); + rc = fwup_add_update_capsule(updates[i], &capsules[j], &cbd_data[j], image); if (EFI_ERROR(rc)) { + /* ignore a failing UX capsule */ if (rc == EFI_UNSUPPORTED && - is_ux_capsule(&updates[i]->info->guid)) + CompareGuid(&updates[i]->info->guid, &ux_capsule_guid) == 0) { + fwup_debug(L"GOP unsuitable: %r", rc); continue; - dprint(L"fwupdate: Could not build update list: %r\n", - rc); + } + fwup_warning(L"Could not build update list: %r", rc); return rc; } j++; } n_updates = j; - dprint(L"n_updates: %d\n", n_updates); + fwup_debug(L"n_updates: %d", n_updates); cbd_data[i].Length = 0; cbd_data[i].Union.ContinuationPointer = 0; - /* - * Step 3: update the state variables. - */ - rc = set_statuses(n_updates, updates); + /* step 3: update the state variables */ + rc = fwup_set_update_statuses(updates); if (EFI_ERROR(rc)) { - print(L"fwupdate: Could not set update status: %r\n", rc); + fwup_warning(L"Could not set update status: %r", rc); return rc; } - /* - * Step 4: apply the capsules. - */ - rc = apply_capsules(capsules, cbd_data, n_updates, &reset_type); + /* step 4: apply the capsules */ + rc = fwup_apply_capsules(capsules, cbd_data, n_updates, &reset_type); if (EFI_ERROR(rc)) { - print(L"fwupdate: Could not apply capsules: %r\n", rc); + fwup_warning(L"Could not apply capsules: %r", rc); return rc; } - /* - * Step 5: if #4 didn't reboot us, do it manually. - */ - dprint(L"fwupdate: Reset System\n"); - if (debugging) - uefi_call_wrapper(BS->Stall, 1, 5 * SECONDS); - - uefi_call_wrapper(BS->Stall, 1, 5 * SECONDS); - uefi_call_wrapper(RT->ResetSystem, 4, reset_type, EFI_SUCCESS, - 0, NULL); + /* step 5: if #4 didn't reboot us, do it manually */ + fwup_info(L"Reset System"); + fwup_msleep(5 * SECONDS); + if (fwup_debug_get_enabled()) + fwup_msleep(30 * SECONDS); + uefi_call_wrapper(RT->ResetSystem, 4, reset_type, EFI_SUCCESS, 0, NULL); return EFI_SUCCESS; } diff --git a/plugins/uefi/efi/hexdump.h b/plugins/uefi/efi/hexdump.h deleted file mode 100644 index 7a96b3851..000000000 --- a/plugins/uefi/efi/hexdump.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2015-2016 Peter Jones - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef STATIC_HEXDUMP_H -#define STATIC_HEXDUMP_H - -static int -__attribute__((__unused__)) -isprint(char c) -{ - if (c < 0x20) - return 0; - if (c > 0x7e) - return 0; - return 1; -} - -static UINTN -__attribute__((__unused__)) -format_hex(UINT8 *data, UINTN size, CHAR16 *buf) -{ - UINTN sz = (UINTN)data % 16; - CHAR16 hexchars[] = L"0123456789abcdef"; - int offset = 0; - unsigned int i; - unsigned int j; - - for (i = 0; i < sz; i++) { - buf[offset++] = L' '; - buf[offset++] = L' '; - buf[offset++] = L' '; - if (i == 7) - buf[offset++] = L' '; - } - for (j = sz; j < 16 && j < size; j++) { - UINT8 d = data[j-sz]; - buf[offset++] = hexchars[(d & 0xf0) >> 4]; - buf[offset++] = hexchars[(d & 0x0f)]; - if (j != 15) - buf[offset++] = L' '; - if (j == 7) - buf[offset++] = L' '; - } - for (i = j; i < 16; i++) { - buf[offset++] = L' '; - buf[offset++] = L' '; - if (i != 15) - buf[offset++] = L' '; - if (i == 7) - buf[offset++] = L' '; - } - buf[offset] = L'\0'; - return j - sz; -} - -static void -__attribute__((__unused__)) -format_text(UINT8 *data, UINTN size, CHAR16 *buf) -{ - UINTN sz = (UINTN)data % 16; - int offset = 0; - unsigned int i; - unsigned int j; - - for (i = 0; i < sz; i++) - buf[offset++] = L' '; - buf[offset++] = L'|'; - for (j = sz; j < 16 && j < size; j++) { - if (isprint(data[j-sz])) - buf[offset++] = data[j-sz]; - else - buf[offset++] = L'.'; - } - buf[offset++] = L'|'; - for (i = j; i < 16; i++) - buf[offset++] = L' '; - buf[offset] = L'\0'; -} - -static void -__attribute__((__unused__)) -hexdump(UINT8 *data, UINTN size) -{ - UINTN display_offset = (UINTN)data & 0xffffffff; - UINTN offset = 0; - //Print(L"hexdump: data=0x%016x size=0x%x\n", data, size); - - while (offset < size) { - CHAR16 hexbuf[49]; - CHAR16 txtbuf[19]; - UINTN sz; - - sz = format_hex(data+offset, size-offset, hexbuf); - if (sz == 0) - return; - uefi_call_wrapper(BS->Stall, 1, 200000); - - format_text(data+offset, size-offset, txtbuf); - Print(L"%08x %s %s\n", display_offset, hexbuf, txtbuf); - uefi_call_wrapper(BS->Stall, 1, 200000); - - display_offset += sz; - offset += sz; - } -} - - -#endif diff --git a/plugins/uefi/efi/meson.build b/plugins/uefi/efi/meson.build index d78aab99a..dbccda03f 100644 --- a/plugins/uefi/efi/meson.build +++ b/plugins/uefi/efi/meson.build @@ -55,6 +55,7 @@ compile_args = ['-Og', '-Wall', '-Werror', '-Wextra', + '-Wvla', '-std=gnu11', '-fpic', '-fshort-wchar', @@ -103,14 +104,29 @@ endif libgcc_file_name = run_command(efi_cc, '-print-libgcc-file-name').stdout().strip() efi_name = 'fwupd@0@.efi'.format(EFI_MACHINE_TYPE_NAME) -o_file = custom_target('fwupdate.o', +o_file1 = custom_target('fwupdate.o', input : 'fwupdate.c', output : 'fwupdate.o', command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@'] + compile_args) +o_file2 = custom_target('fwup-debug.o', + input : 'fwup-debug.c', + output : 'fwup-debug.o', + command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@'] + + compile_args) +o_file3 = custom_target('fwup-efi.o', + input : 'fwup-efi.c', + output : 'fwup-efi.o', + command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@'] + + compile_args) +o_file4 = custom_target('fwup-common.o', + input : 'fwup-common.c', + output : 'fwup-common.o', + command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@'] + + compile_args) so = custom_target('fwup.so', - input : o_file, + input : [o_file1, o_file2, o_file3, o_file4], output : 'fwup.so', command : [efi_ld, '-o', '@OUTPUT@'] + efi_ldflags + ['@INPUT@'] + diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 44876910c..17720d3ac 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -38,7 +38,9 @@ fu_plugin_init (FuPlugin *plugin) FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); data->bgrt = fu_uefi_bgrt_new (); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.uefi.capsule"); fu_plugin_add_compile_version (plugin, "com.redhat.efivar", EFIVAR_LIBRARY_VERSION); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } void @@ -157,6 +159,15 @@ fu_plugin_uefi_get_splash_data (guint width, guint height, GError **error) return g_bytes_new_take (g_steal_pointer (&buf), buf_idx); } +static guint8 +fu_plugin_uefi_calc_checksum (const guint8 *buf, gsize sz) +{ + guint8 csum = 0; + for (gsize i = 0; i < sz; i++) + csum += buf[i]; + return csum; +} + static gboolean fu_plugin_uefi_write_splash_data (FuPlugin *plugin, GBytes *blob, GError **error) { @@ -165,7 +176,8 @@ fu_plugin_uefi_write_splash_data (FuPlugin *plugin, GBytes *blob, GError **error gsize buf_size = g_bytes_get_size (blob); gssize size; guint32 height, width; - efi_ux_capsule_header_t header; + guint8 csum = 0; + efi_ux_capsule_header_t header = { 0 }; efi_capsule_header_t capsule_header = { .flags = EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET, .guid = efi_guid_ux_capsule, @@ -202,9 +214,8 @@ fu_plugin_uefi_write_splash_data (FuPlugin *plugin, GBytes *blob, GError **error capsule_header.capsule_image_size = g_bytes_get_size (blob) + sizeof(efi_capsule_header_t) + - sizeof(header); + sizeof(efi_ux_capsule_header_t); - memset (&header, '\0', sizeof(header)); header.version = 1; header.image_type = 0; header.reserved = 0; @@ -212,6 +223,15 @@ fu_plugin_uefi_write_splash_data (FuPlugin *plugin, GBytes *blob, GError **error header.y_offset = fu_uefi_bgrt_get_yoffset (data->bgrt) + fu_uefi_bgrt_get_height (data->bgrt); + /* header, payload and image has to add to zero */ + csum += fu_plugin_uefi_calc_checksum ((guint8 *) &capsule_header, + sizeof(capsule_header)); + csum += fu_plugin_uefi_calc_checksum ((guint8 *) &header, + sizeof(header)); + csum += fu_plugin_uefi_calc_checksum (g_bytes_get_data (blob, NULL), + g_bytes_get_size (blob)); + header.checksum = 0x100 - csum; + /* write capsule file */ size = g_output_stream_write (ostream, &capsule_header, capsule_header.header_size, NULL, error); @@ -377,6 +397,10 @@ fu_plugin_update (FuPlugin *plugin, if (!fu_device_write_firmware (device, blob_fw, error)) return FALSE; + /* record if we had an invalid header during update */ + str = fu_uefi_missing_capsule_header (device) ? "True" : "False"; + fu_plugin_add_report_metadata (plugin, "MissingCapsuleHeader", str); + /* record boot information to system log for future debugging */ efibootmgr_path = fu_common_find_program_in_path ("efibootmgr", NULL); if (efibootmgr_path != NULL) { @@ -412,7 +436,7 @@ fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) } } -static AsVersionParseFlag +static FuVersionFormat fu_plugin_uefi_get_version_format_for_type (FuPlugin *plugin, FuUefiDeviceKind device_kind) { const gchar *content; @@ -421,21 +445,19 @@ fu_plugin_uefi_get_version_format_for_type (FuPlugin *plugin, FuUefiDeviceKind d /* we have no information for devices */ if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + return FU_VERSION_FORMAT_TRIPLET; content = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); if (content == NULL) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + return FU_VERSION_FORMAT_TRIPLET; /* any quirks match */ group = g_strdup_printf ("SmbiosManufacturer=%s", content); quirk = fu_plugin_lookup_quirk_by_id (plugin, group, FU_QUIRKS_UEFI_VERSION_FORMAT); - if (g_strcmp0 (quirk, "quad") == 0) - return AS_VERSION_PARSE_FLAG_NONE; - - /* fall back */ - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + if (quirk == NULL) + return FU_VERSION_FORMAT_TRIPLET; + return fu_common_version_format_from_string (quirk); } static const gchar * @@ -478,37 +500,29 @@ static gboolean fu_plugin_uefi_coldplug_device (FuPlugin *plugin, FuUefiDevice *dev, GError **error) { FuUefiDeviceKind device_kind; - AsVersionParseFlag parse_flags; - guint32 version_raw; - g_autofree gchar *name = NULL; - g_autofree gchar *version_lowest = NULL; - g_autofree gchar *version = NULL; + FuVersionFormat version_format; - /* add details to the device */ + /* set default version format */ device_kind = fu_uefi_device_get_kind (dev); - parse_flags = fu_plugin_uefi_get_version_format_for_type (plugin, device_kind); - version_raw = fu_uefi_device_get_version (dev); - version = as_utils_version_from_uint32 (version_raw, parse_flags); - fu_device_set_version (dev, version); - name = fu_plugin_uefi_get_name_for_type (plugin, fu_uefi_device_get_kind (dev)); - if (name != NULL) - fu_device_set_name (FU_DEVICE (dev), name); - version_raw = fu_uefi_device_get_version_lowest (dev); - if (version_raw != 0) { - version_lowest = as_utils_version_from_uint32 (version_raw, - parse_flags); - fu_device_set_version_lowest (FU_DEVICE (dev), version_lowest); + version_format = fu_plugin_uefi_get_version_format_for_type (plugin, device_kind); + fu_device_set_version_format (FU_DEVICE (dev), version_format); + + /* probe to get add GUIDs (and hence any quirk fixups) */ + if (!fu_device_probe (FU_DEVICE (dev), error)) + return FALSE; + + /* set this flag for all Lenovo hardware */ + if (fu_plugin_check_hwid (plugin, "6de5d951-d755-576b-bd09-c5cf66b27234")) { + fu_device_set_custom_flags (FU_DEVICE (dev), "use-legacy-bootmgr-desc"); + fu_plugin_add_report_metadata (plugin, "BootMgrDesc", "legacy"); } - fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_REQUIRE_AC); - if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) { - /* nothing better in the icon naming spec */ - fu_device_add_icon (FU_DEVICE (dev), "audio-card"); - } else { - /* this is probably system firmware */ - fu_device_add_icon (FU_DEVICE (dev), "computer"); - fu_device_add_guid (FU_DEVICE (dev), "main-system-firmware"); + + /* set fallback name if nothing else is set */ + if (fu_device_get_name (FU_DEVICE (dev)) == 0) { + g_autofree gchar *name = NULL; + name = fu_plugin_uefi_get_name_for_type (plugin, fu_uefi_device_get_kind (dev)); + if (name != NULL) + fu_device_set_name (FU_DEVICE (dev), name); } /* success */ @@ -742,7 +756,13 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) /* add each device */ for (guint i = 0; i < entries->len; i++) { const gchar *path = g_ptr_array_index (entries, i); - g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_entry (path); + g_autoptr(GError) error_parse = NULL; + g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_entry (path, &error_parse); + if (dev == NULL) { + g_warning ("failed to add %s: %s", path, error_parse->message); + continue; + } + fu_device_set_quirks (FU_DEVICE (dev), fu_plugin_get_quirks (plugin)); if (!fu_plugin_uefi_coldplug_device (plugin, dev, error)) return FALSE; if (error_esp != NULL) { diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index 6f6a0d8f5..da71326a9 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -13,8 +13,73 @@ #include "fu-uefi-bgrt.h" #include "fu-uefi-common.h" #include "fu-uefi-device.h" +#include "fu-uefi-pcrs.h" #include "fu-uefi-vars.h" +static void +fu_uefi_pcrs_1_2_func (void) +{ + gboolean ret; + g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + g_autoptr(GPtrArray) pcrXs = NULL; + + ret = fu_uefi_pcrs_setup (pcrs, &error); + g_assert_no_error (error); + g_assert_true (ret); + pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); + g_assert_nonnull (pcr0s); + g_assert_cmpint (pcr0s->len, ==, 1); + pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); + g_assert_nonnull (pcrXs); + g_assert_cmpint (pcrXs->len, ==, 0); +} + +static void +fu_uefi_pcrs_2_0_func (void) +{ + gboolean ret; + g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + g_autoptr(GPtrArray) pcrXs = NULL; + + g_setenv ("FWUPD_UEFI_TPM2_YAML_DATA", + "sha1 :\n" + " 0 : cbd9e4112727bc75761001abcb2dddd87a66caf5\n" + "sha256 :\n" + " 0 : 122de8b579cce17b0703ca9f9716d6f99125af9569e7303f51ea7f85d317f01e\n", TRUE); + + ret = fu_uefi_pcrs_setup (pcrs, &error); + g_assert_no_error (error); + g_assert_true (ret); + pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); + g_assert_nonnull (pcr0s); + g_assert_cmpint (pcr0s->len, ==, 2); + pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); + g_assert_nonnull (pcrXs); + g_assert_cmpint (pcrXs->len, ==, 0); +} + +static void +fu_uefi_pcrs_2_0_failure_func (void) +{ + gboolean ret; + g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); + g_autoptr(GError) error = NULL; + + g_setenv ("FWUPD_UEFI_TPM2_YAML_DATA", + "Something is not working properly!\n" + "999:hello\n" + "0:dave\n" + "\n", TRUE); + + ret = fu_uefi_pcrs_setup (pcrs, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); + g_assert_false (ret); +} + static void fu_uefi_ucs2_func (void) { @@ -85,11 +150,13 @@ fu_uefi_device_func (void) { g_autofree gchar *fn = NULL; g_autoptr(FuUefiDevice) dev = NULL; + g_autoptr(GError) error = NULL; fn = fu_test_get_filename (TESTDATADIR, "efi/esrt/entries/entry0"); g_assert (fn != NULL); - dev = fu_uefi_device_new_from_entry (fn); + dev = fu_uefi_device_new_from_entry (fn, &error); g_assert_nonnull (dev); + g_assert_no_error (error); g_assert_cmpint (fu_uefi_device_get_kind (dev), ==, FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE); g_assert_cmpstr (fu_uefi_device_get_guid (dev), ==, "ddc0ee61-e7f0-4e7d-acc5-c070a398838e"); @@ -184,7 +251,12 @@ fu_uefi_plugin_func (void) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < entries->len; i++) { const gchar *path = g_ptr_array_index (entries, i); - g_autoptr(FuUefiDevice) dev_tmp = fu_uefi_device_new_from_entry (path); + g_autoptr(GError) error_local = NULL; + g_autoptr(FuUefiDevice) dev_tmp = fu_uefi_device_new_from_entry (path, &error_local); + if (dev_tmp == NULL) { + g_debug ("failed to add %s: %s", path, error_local->message); + continue; + } g_ptr_array_add (devices, g_object_ref (dev_tmp)); } g_assert_cmpint (devices->len, ==, 2); @@ -220,7 +292,8 @@ fu_uefi_update_info_func (void) fn = fu_test_get_filename (TESTDATADIR, "efi/esrt/entries/entry0"); g_assert (fn != NULL); - dev = fu_uefi_device_new_from_entry (fn); + dev = fu_uefi_device_new_from_entry (fn, &error); + g_assert_no_error (error); g_assert_nonnull (dev); g_assert_cmpint (fu_uefi_device_get_kind (dev), ==, FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE); g_assert_cmpstr (fu_uefi_device_get_guid (dev), ==, "ddc0ee61-e7f0-4e7d-acc5-c070a398838e"); @@ -242,11 +315,15 @@ main (int argc, char **argv) g_test_init (&argc, &argv, NULL); g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE); g_setenv ("FWUPD_SYSFSDRIVERDIR", TESTDATADIR, TRUE); + g_setenv ("FWUPD_SYSFSTPMDIR", TESTDATADIR, TRUE); /* only critical and error are fatal */ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); /* tests go here */ + g_test_add_func ("/uefi/pcrs1.2", fu_uefi_pcrs_1_2_func); + g_test_add_func ("/uefi/pcrs2.0", fu_uefi_pcrs_2_0_func); + g_test_add_func ("/uefi/pcrs2.0{failure}", fu_uefi_pcrs_2_0_failure_func); g_test_add_func ("/uefi/ucs2", fu_uefi_ucs2_func); g_test_add_func ("/uefi/variable", fu_uefi_vars_func); g_test_add_func ("/uefi/bgrt", fu_uefi_bgrt_func); diff --git a/plugins/uefi/fu-uefi-bgrt.c b/plugins/uefi/fu-uefi-bgrt.c index 52d489553..66b2c4f9e 100644 --- a/plugins/uefi/fu-uefi-bgrt.c +++ b/plugins/uefi/fu-uefi-bgrt.c @@ -24,7 +24,6 @@ gboolean fu_uefi_bgrt_setup (FuUefiBgrt *self, GError **error) { gsize sz = 0; - guint64 status; guint64 type; guint64 version; g_autofree gchar *bgrtdir = NULL; @@ -43,14 +42,6 @@ fu_uefi_bgrt_setup (FuUefiBgrt *self, GError **error) "BGRT is not supported"); return FALSE; } - status = fu_uefi_read_file_as_uint64 (bgrtdir, "status"); - if (status != 1) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "BGRT status was %" G_GUINT64_FORMAT, status); - return FALSE; - } type = fu_uefi_read_file_as_uint64 (bgrtdir, "type"); if (type != 0) { g_set_error (error, diff --git a/plugins/uefi/fu-uefi-bootmgr.c b/plugins/uefi/fu-uefi-bootmgr.c index c70f972cb..a67ea1499 100644 --- a/plugins/uefi/fu-uefi-bootmgr.c +++ b/plugins/uefi/fu-uefi-bootmgr.c @@ -108,7 +108,7 @@ fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_si efidp found_dp; g_autofree guint8 *var_data_tmp = NULL; - if (efi_guid_cmp (guid, &efi_guid_global)) + if (efi_guid_cmp (guid, &efi_guid_global) != 0) continue; rc = sscanf (name, "Boot%hX%n", &entry, &scanned); if (rc < 0) { @@ -292,7 +292,10 @@ fu_uefi_copy_asset (const gchar *source, const gchar *target, GError **error) } gboolean -fu_uefi_bootmgr_bootnext (const gchar *esp_path, FuUefiBootmgrFlags flags, GError **error) +fu_uefi_bootmgr_bootnext (const gchar *esp_path, + const gchar *description, + FuUefiBootmgrFlags flags, + GError **error) { gboolean use_fwup_path = FALSE; gsize loader_sz = 0; @@ -382,7 +385,7 @@ fu_uefi_bootmgr_bootnext (const gchar *esp_path, FuUefiBootmgrFlags flags, GErro return FALSE; } - label = g_strdup ("Linux Firmware Updater"); + label = g_strdup (description); sz = efi_loadopt_create (opt, opt_size, attributes, (efidp)dp_buf, dp_size, (guint8 *)label, diff --git a/plugins/uefi/fu-uefi-bootmgr.h b/plugins/uefi/fu-uefi-bootmgr.h index 34c9049b4..07622cd67 100644 --- a/plugins/uefi/fu-uefi-bootmgr.h +++ b/plugins/uefi/fu-uefi-bootmgr.h @@ -20,6 +20,7 @@ typedef enum { } FuUefiBootmgrFlags; gboolean fu_uefi_bootmgr_bootnext (const gchar *esp_path, + const gchar *description, FuUefiBootmgrFlags flags, GError **error); diff --git a/plugins/uefi/fu-uefi-device.c b/plugins/uefi/fu-uefi-device.c index 64c314158..34bc4ac0d 100644 --- a/plugins/uefi/fu-uefi-device.c +++ b/plugins/uefi/fu-uefi-device.c @@ -16,6 +16,7 @@ #include "fu-uefi-common.h" #include "fu-uefi-device.h" #include "fu-uefi-bootmgr.h" +#include "fu-uefi-pcrs.h" #include "fu-uefi-vars.h" struct _FuUefiDevice { @@ -28,6 +29,7 @@ struct _FuUefiDevice { FuUefiDeviceStatus last_attempt_status; guint32 last_attempt_version; guint64 fmp_hardware_instance; + gboolean missing_header; }; G_DEFINE_TYPE (FuUefiDevice, fu_uefi_device, FU_TYPE_DEVICE) @@ -289,16 +291,75 @@ fu_uefi_device_build_dp_buf (const gchar *path, gsize *bufsz, GError **error) return g_steal_pointer (&dp_buf); } +static GBytes * +fu_uefi_device_fixup_firmware (FuDevice *device, GBytes *fw, GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + gsize fw_length; + efi_guid_t esrt_guid; + efi_guid_t payload_guid; + const gchar *data = g_bytes_get_data (fw, &fw_length); + self->missing_header = FALSE; + + /* convert to EFI GUIDs */ + if (efi_str_to_guid (fu_uefi_device_get_guid (self), &esrt_guid) < 0) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Invalid ESRT GUID"); + return NULL; + } + if (fw_length < sizeof(efi_guid_t)) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, + "Invalid payload"); + return NULL; + } + memcpy (&payload_guid, data, sizeof(efi_guid_t)); + + /* ESRT header matches payload */ + if (efi_guid_cmp (&esrt_guid, &payload_guid) == 0) { + g_debug ("ESRT matches payload GUID"); + return g_bytes_new_from_bytes (fw, 0, fw_length); + /* FMP payload */ + } else if (fu_uefi_device_get_kind (self) == FU_UEFI_DEVICE_KIND_FMP) { + g_debug ("performing FMP update"); + return g_bytes_new_from_bytes (fw, 0, fw_length); + /* Missing, add a header */ + } else { + guint header_size = getpagesize(); + guint8 *new_data = g_malloc (fw_length + header_size); + guint8 *capsule = new_data + header_size; + efi_capsule_header_t *header = (efi_capsule_header_t *) new_data; + + g_warning ("missing or invalid embedded capsule header"); + self->missing_header = TRUE; + header->flags = self->capsule_flags; + header->header_size = header_size; + header->capsule_image_size = fw_length + header_size; + memcpy (&header->guid, &esrt_guid, sizeof (efi_guid_t)); + memcpy (capsule, data, fw_length); + + return g_bytes_new_take (new_data, fw_length + header_size); + } +} + +gboolean +fu_uefi_missing_capsule_header (FuDevice *device) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + return self->missing_header; +} + static gboolean fu_uefi_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) { FuUefiDevice *self = FU_UEFI_DEVICE (device); FuUefiBootmgrFlags flags = FU_UEFI_BOOTMGR_FLAG_NONE; + const gchar *bootmgr_desc = "Linux Firmware Updater"; const gchar *esp_path = fu_device_get_metadata (device, "EspPath"); efi_guid_t guid; efi_update_info_t info; gsize datasz = 0; gsize dp_bufsz = 0; + g_autoptr(GBytes) fixed_fw = NULL; g_autofree gchar *basename = NULL; g_autofree gchar *directory = NULL; g_autofree gchar *fn = NULL; @@ -320,7 +381,10 @@ fu_uefi_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) fn = g_build_filename (directory, "fw", basename, NULL); if (!fu_common_mkdir_parent (fn, error)) return FALSE; - if (!fu_common_set_contents_bytes (fn, fw, error)) + fixed_fw = fu_uefi_device_fixup_firmware (device, fw, error); + if (fixed_fw == NULL) + return FALSE; + if (!fu_common_set_contents_bytes (fn, fixed_fw, error)) return FALSE; /* set the blob header shared with fwupd.efi */ @@ -360,13 +424,116 @@ fu_uefi_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) /* update the firmware before the bootloader runs */ if (fu_device_get_metadata_boolean (device, "RequireShimForSecureBoot")) flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB; - if (!fu_uefi_bootmgr_bootnext (esp_path, flags, error)) + + /* some legacy devices use the old name to deduplicate boot entries */ + if (fu_device_has_custom_flag (device, "use-legacy-bootmgr-desc")) + bootmgr_desc = "Linux-Firmware-Updater"; + if (!fu_uefi_bootmgr_bootnext (esp_path, bootmgr_desc, flags, error)) return FALSE; /* success! */ return TRUE; } +static gboolean +fu_uefi_device_add_system_checksum (FuDevice *device, GError **error) +{ + g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + + /* get all the PCRs */ + if (!fu_uefi_pcrs_setup (pcrs, &error_local)) { + if (g_error_matches (error_local, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED)) { + g_debug ("%s", error_local->message); + return TRUE; + } + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + + /* get all the PCR0s */ + pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); + if (pcr0s->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no PCR0s detected"); + return FALSE; + } + for (guint i = 0; i < pcr0s->len; i++) { + const gchar *checksum = g_ptr_array_index (pcr0s, i); + fu_device_add_checksum (device, checksum); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uefi_device_probe (FuDevice *device, GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + FuVersionFormat version_format; + g_autofree gchar *guid_devid = NULL; + g_autofree gchar *guid_strup = NULL; + g_autofree gchar *version_lowest = NULL; + g_autofree gchar *version = NULL; + + /* broken sysfs? */ + if (self->fw_class == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to read fw_class"); + return FALSE; + } + + /* add GUID first, as quirks may set the version format */ + fu_device_add_guid (device, self->fw_class); + + /* set versions */ + version_format = fu_device_get_version_format (device); + version = fu_common_version_from_uint32 (self->fw_version, version_format); + fu_device_set_version (device, version); + if (self->fw_version_lowest != 0) { + version_lowest = fu_common_version_from_uint32 (self->fw_version_lowest, + version_format); + 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); + + /* add icons */ + if (self->kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) { + /* nothing better in the icon naming spec */ + fu_device_add_icon (device, "audio-card"); + } else { + /* this is probably system firmware */ + fu_device_add_icon (device, "computer"); + fu_device_add_guid (device, "main-system-firmware"); + } + + /* set the PCR0 as the device checksum */ + if (self->kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) { + g_autoptr(GError) error_local = NULL; + if (!fu_uefi_device_add_system_checksum (device, &error_local)) + g_warning ("Failed to get PCR0s: %s", error_local->message); + } + + /* Windows seems to be case insensitive, but for convenience we'll + * match the upper case values typically specified in the .inf file */ + guid_strup = g_ascii_strup (self->fw_class, -1); + guid_devid = g_strdup_printf ("UEFI\\RES_{%s}", guid_strup); + fu_device_add_guid (device, guid_devid); + return TRUE; +} + static void fu_uefi_device_init (FuUefiDevice *self) { @@ -389,30 +556,14 @@ fu_uefi_device_class_init (FuUefiDeviceClass *klass) FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); object_class->finalize = fu_uefi_device_finalize; klass_device->to_string = fu_uefi_device_to_string; + klass_device->probe = fu_uefi_device_probe; klass_device->write_firmware = fu_uefi_device_write_firmware; } -static void -fu_uefi_device_add_win10_guid (FuUefiDevice *self) -{ - g_autofree gchar *guid_devid = NULL; - g_autofree gchar *guid_strup = NULL; - - /* broken sysfs? */ - if (self->fw_class == NULL) - return; - - /* windows seems to be case insensitive, but for convenience we'll - * match the upper case values typically specified in the .inf file */ - guid_strup = g_ascii_strup (self->fw_class, -1); - guid_devid = g_strdup_printf ("UEFI\\RES_{%s}", guid_strup); - fu_device_add_guid (FU_DEVICE (self), guid_devid); -} - FuUefiDevice * -fu_uefi_device_new_from_entry (const gchar *entry_path) +fu_uefi_device_new_from_entry (const gchar *entry_path, GError **error) { - FuUefiDevice *self; + g_autoptr(FuUefiDevice) self = NULL; g_autofree gchar *fw_class_fn = NULL; g_autofree gchar *id = NULL; @@ -423,10 +574,8 @@ fu_uefi_device_new_from_entry (const gchar *entry_path) /* read values from sysfs */ fw_class_fn = g_build_filename (entry_path, "fw_class", NULL); - if (g_file_get_contents (fw_class_fn, &self->fw_class, NULL, NULL)) { + if (g_file_get_contents (fw_class_fn, &self->fw_class, NULL, NULL)) g_strdelimit (self->fw_class, "\n", '\0'); - fu_device_add_guid (FU_DEVICE (self), self->fw_class); - } self->capsule_flags = fu_uefi_read_file_as_uint64 (entry_path, "capsule_flags"); self->kind = fu_uefi_read_file_as_uint64 (entry_path, "fw_type"); self->fw_version = fu_uefi_read_file_as_uint64 (entry_path, "fw_version"); @@ -444,10 +593,16 @@ fu_uefi_device_new_from_entry (const gchar *entry_path) self->fw_class, self->fmp_hardware_instance); fu_device_set_id (FU_DEVICE (self), id); - /* this is the DeviceID used in Windows 10 */ - fu_uefi_device_add_win10_guid (self); + /* this is invalid */ + if (!fu_common_guid_is_valid (self->fw_class)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ESRT GUID '%s' was not valid", self->fw_class); + return NULL; + } - return self; + return g_steal_pointer (&self); } FuUefiDevice * @@ -467,9 +622,6 @@ fu_uefi_device_new_from_dev (FuDevice *dev) self->capsule_flags = 0; /* FIXME? */ self->fw_version = 0; /* FIXME? */ g_assert (self->fw_class != NULL); - - /* this is the DeviceID used in Windows 10 */ - fu_uefi_device_add_win10_guid (self); return self; } diff --git a/plugins/uefi/fu-uefi-device.h b/plugins/uefi/fu-uefi-device.h index 0bb2d6cfc..98aee51b4 100644 --- a/plugins/uefi/fu-uefi-device.h +++ b/plugins/uefi/fu-uefi-device.h @@ -40,7 +40,8 @@ typedef enum { } FuUefiDeviceStatus; FuUefiDevice *fu_uefi_device_new_from_guid (const gchar *guid); -FuUefiDevice *fu_uefi_device_new_from_entry (const gchar *entry_path); +FuUefiDevice *fu_uefi_device_new_from_entry (const gchar *entry_path, + GError **error); FuUefiDevice *fu_uefi_device_new_from_dev (FuDevice *dev); gboolean fu_uefi_device_clear_status (FuUefiDevice *self, GError **error); @@ -56,6 +57,7 @@ const gchar *fu_uefi_device_kind_to_string (FuUefiDeviceKind kind); const gchar *fu_uefi_device_status_to_string (FuUefiDeviceStatus status); FuUefiUpdateInfo *fu_uefi_device_load_update_info (FuUefiDevice *self, GError **error); +gboolean fu_uefi_missing_capsule_header (FuDevice *device); G_END_DECLS diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c new file mode 100644 index 000000000..a670e4d0a --- /dev/null +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" +#include "fu-uefi-pcrs.h" + +typedef struct { + guint idx; + gchar *checksum; +} FuUefiPcrItem; + +struct _FuUefiPcrs { + GObject parent_instance; + GPtrArray *items; /* of FuUefiPcrItem */ +}; + +G_DEFINE_TYPE (FuUefiPcrs, fu_uefi_pcrs, G_TYPE_OBJECT) + +static void +fu_uefi_pcrs_parse_line (const gchar *line, gpointer user_data) +{ + FuUefiPcrs *self = FU_UEFI_PCRS (user_data); + FuUefiPcrItem *item; + guint64 idx; + g_autofree gchar *idxstr = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GString) str = NULL; + + /* split into index:hash */ + if (line == NULL || line[0] == '\0') + return; + split = g_strsplit (line, ":", 2); + if (g_strv_length (split) != 2) { + g_debug ("unexpected format, skipping: %s", line); + return; + } + + /* get index */ + idxstr = fu_common_strstrip (split[0]); + idx = fu_common_strtoull (idxstr); + if (idx > 64) { + g_debug ("unexpected index, skipping: %s", idxstr); + return; + } + + /* parse hash */ + str = g_string_new (split[1]); + if (str->len < 16) + return; + fu_common_string_replace (str, " ", ""); + g_string_ascii_down (str); + item = g_new0 (FuUefiPcrItem, 1); + item->idx = idx; + item->checksum = g_string_free (g_steal_pointer (&str), FALSE); + g_ptr_array_add (self->items, item); + g_debug ("added PCR-%02u=%s", item->idx, item->checksum); +} + +static gboolean +fu_uefi_pcrs_setup_dummy (FuUefiPcrs *self, const gchar *test_yaml, GError **error) +{ + g_auto(GStrv) lines = g_strsplit (test_yaml, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) + fu_uefi_pcrs_parse_line (lines[i], self); + return TRUE; +} + +static gboolean +fu_uefi_pcrs_setup_tpm12 (FuUefiPcrs *self, const gchar *fn_pcrs, GError **error) +{ + g_auto(GStrv) lines = NULL; + g_autofree gchar *buf_pcrs = NULL; + + /* get entire contents */ + if (!g_file_get_contents (fn_pcrs, &buf_pcrs, NULL, error)) + return FALSE; + + /* find PCR lines */ + lines = g_strsplit (buf_pcrs, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + if (g_str_has_prefix (lines[i], "PCR-")) + fu_uefi_pcrs_parse_line (lines[i] + 4, self); + } + return TRUE; +} + +static gboolean +fu_uefi_pcrs_setup_tpm20 (FuUefiPcrs *self, const gchar *argv0, GError **error) +{ + const gchar *argv[] = { argv0, NULL }; + return fu_common_spawn_sync (argv, fu_uefi_pcrs_parse_line, self, NULL, error); +} + +gboolean +fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error) +{ + g_autofree gchar *devpath = NULL; + g_autofree gchar *sysfstpmdir = NULL; + g_autofree gchar *fn_pcrs = NULL; + const gchar *test_yaml = g_getenv ("FWUPD_UEFI_TPM2_YAML_DATA"); + + g_return_val_if_fail (FU_IS_UEFI_PCRS (self), FALSE); + + /* check the TPM device exists at all */ + sysfstpmdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_TPM); + devpath = g_build_filename (sysfstpmdir, "tpm0", NULL); + if (!g_file_test (devpath, G_FILE_TEST_EXISTS)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "no TPM device found"); + return FALSE; + } + fn_pcrs = g_build_filename (devpath, "pcrs", NULL); + + /* fake device */ + if (test_yaml != NULL) { + if (!fu_uefi_pcrs_setup_dummy (self, test_yaml, error)) + return FALSE; + + /* look for TPM 1.2 */ + } else if (g_file_test (fn_pcrs, G_FILE_TEST_EXISTS)) { + if (!fu_uefi_pcrs_setup_tpm12 (self, fn_pcrs, error)) + return FALSE; + + /* assume TPM 2.0 */ + } else { + g_autofree gchar *argv0 = NULL; + + /* old name, then new name */ + argv0 = fu_common_find_program_in_path ("tpm2_listpcrs", NULL); + if (argv0 == NULL) + argv0 = fu_common_find_program_in_path ("tpm2_pcrlist", error); + if (argv0 == NULL) + return FALSE; + if (!fu_uefi_pcrs_setup_tpm20 (self, argv0, error)) + return FALSE; + } + + /* check we got anything */ + if (self->items->len == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "no TPMxx measurements found"); + return FALSE; + } + + /* success */ + return TRUE; +} + +GPtrArray * +fu_uefi_pcrs_get_checksums (FuUefiPcrs *self, guint idx) +{ + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); + g_return_val_if_fail (FU_IS_UEFI_PCRS (self), NULL); + for (guint i = 0; i < self->items->len; i++) { + FuUefiPcrItem *item = g_ptr_array_index (self->items, i); + if (item->idx == idx) + g_ptr_array_add (array, g_strdup (item->checksum)); + } + return g_steal_pointer (&array); +} + +static void +fu_uefi_pcrs_item_free (FuUefiPcrItem *item) +{ + g_free (item->checksum); + g_free (item); +} + +static void +fu_uefi_pcrs_finalize (GObject *object) +{ + FuUefiPcrs *self = FU_UEFI_PCRS (object); + g_ptr_array_unref (self->items); + G_OBJECT_CLASS (fu_uefi_pcrs_parent_class)->finalize (object); +} + +static void +fu_uefi_pcrs_class_init (FuUefiPcrsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_uefi_pcrs_finalize; +} + +static void +fu_uefi_pcrs_init (FuUefiPcrs *self) +{ + self->items = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_uefi_pcrs_item_free); +} + +FuUefiPcrs * +fu_uefi_pcrs_new (void) +{ + FuUefiPcrs *self; + self = g_object_new (FU_TYPE_UEFI_PCRS, NULL); + return FU_UEFI_PCRS (self); +} diff --git a/plugins/uefi/fu-uefi-pcrs.h b/plugins/uefi/fu-uefi-pcrs.h new file mode 100644 index 000000000..9837a9add --- /dev/null +++ b/plugins/uefi/fu-uefi-pcrs.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_UEFI_PCRS_H +#define __FU_UEFI_PCRS_H + +G_BEGIN_DECLS + +#define FU_TYPE_UEFI_PCRS (fu_uefi_pcrs_get_type ()) +G_DECLARE_FINAL_TYPE (FuUefiPcrs, fu_uefi_pcrs, FU, UEFI_PCRS, GObject) + +FuUefiPcrs *fu_uefi_pcrs_new (void); +gboolean fu_uefi_pcrs_setup (FuUefiPcrs *self, + GError **error); +GPtrArray *fu_uefi_pcrs_get_checksums (FuUefiPcrs *self, + guint idx); + +G_END_DECLS + +#endif /* __FU_UEFI_PCRS_H */ diff --git a/plugins/uefi/fu-uefi-tool.c b/plugins/uefi/fu-uefi-tool.c index 74a37c123..94ab5f2e5 100644 --- a/plugins/uefi/fu-uefi-tool.c +++ b/plugins/uefi/fu-uefi-tool.c @@ -216,7 +216,13 @@ main (int argc, char *argv[]) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < entries->len; i++) { const gchar *path = g_ptr_array_index (entries, i); - g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_entry (path); + g_autoptr(GError) error_parse = NULL; + g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_entry (path, &error_parse); + if (dev == NULL) { + g_warning ("failed to parse %s: %s", + path, error_parse->message); + continue; + } fu_device_set_metadata (FU_DEVICE (dev), "EspPath", esp_path); g_ptr_array_add (devices, g_object_ref (dev)); } diff --git a/plugins/uefi/meson.build b/plugins/uefi/meson.build index 140418a8a..2492e4eca 100644 --- a/plugins/uefi/meson.build +++ b/plugins/uefi/meson.build @@ -3,7 +3,12 @@ subdir('efi') cargs = ['-DG_LOG_DOMAIN="FuPluginUefi"'] cargs += '-DEFI_APP_LOCATION_BUILD="' + app.full_path() + '"' +install_data(['uefi.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_uefi', + fu_hash, sources : [ 'fu-plugin-uefi.c', 'fu-uefi-bgrt.c', @@ -11,6 +16,7 @@ shared_module('fu_plugin_uefi', 'fu-uefi-bootmgr.c', 'fu-uefi-common.c', 'fu-uefi-device.c', + 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', 'fu-uefi-vars.c', ], @@ -21,6 +27,9 @@ shared_module('fu_plugin_uefi', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, @@ -32,6 +41,7 @@ shared_module('fu_plugin_uefi', executable( 'fwupdate', resources_src, + fu_hash, sources : [ 'fu-uefi-tool.c', 'fu-uefi-bgrt.c', @@ -39,6 +49,7 @@ executable( 'fu-uefi-bootmgr.c', 'fu-uefi-common.c', 'fu-uefi-device.c', + 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', 'fu-uefi-vars.c', ], @@ -48,7 +59,7 @@ executable( include_directories('../../libfwupd'), ], dependencies : [ - appstream_glib, + libxmlb, giounix, gusb, gudev, @@ -56,7 +67,6 @@ executable( efiboot, ], link_with : [ - fwupd, libfwupdprivate, ], install : true, @@ -79,6 +89,7 @@ if get_option('tests') 'fu-uefi-bootmgr.c', 'fu-uefi-common.c', 'fu-uefi-device.c', + 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', 'fu-uefi-vars.c', 'fu-ucs2.c', @@ -94,7 +105,6 @@ if get_option('tests') efiboot, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs diff --git a/plugins/uefi/tests/efi/esrt/entries/entry2/capsule_flags b/plugins/uefi/tests/efi/esrt/entries/entry2/capsule_flags new file mode 120000 index 000000000..24b0ea1b5 --- /dev/null +++ b/plugins/uefi/tests/efi/esrt/entries/entry2/capsule_flags @@ -0,0 +1 @@ +../entry1/capsule_flags \ No newline at end of file diff --git a/plugins/uefi/tests/efi/esrt/entries/entry2/fw_class b/plugins/uefi/tests/efi/esrt/entries/entry2/fw_class new file mode 100644 index 000000000..44964b11f --- /dev/null +++ b/plugins/uefi/tests/efi/esrt/entries/entry2/fw_class @@ -0,0 +1 @@ +00000000-0000-0000-0000-000000000000 diff --git a/plugins/uefi/tests/efi/esrt/entries/entry2/fw_type b/plugins/uefi/tests/efi/esrt/entries/entry2/fw_type new file mode 120000 index 000000000..d7a6438a2 --- /dev/null +++ b/plugins/uefi/tests/efi/esrt/entries/entry2/fw_type @@ -0,0 +1 @@ +../entry1/fw_type \ No newline at end of file diff --git a/plugins/uefi/tests/efi/esrt/entries/entry2/fw_version b/plugins/uefi/tests/efi/esrt/entries/entry2/fw_version new file mode 120000 index 000000000..3d6ce82dc --- /dev/null +++ b/plugins/uefi/tests/efi/esrt/entries/entry2/fw_version @@ -0,0 +1 @@ +../entry1/fw_version \ No newline at end of file diff --git a/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_status b/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_status new file mode 120000 index 000000000..d8d4cb389 --- /dev/null +++ b/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_status @@ -0,0 +1 @@ +../entry1/last_attempt_status \ No newline at end of file diff --git a/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_version b/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_version new file mode 120000 index 000000000..639dcb185 --- /dev/null +++ b/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_version @@ -0,0 +1 @@ +../entry1/last_attempt_version \ No newline at end of file diff --git a/plugins/uefi/tests/efi/esrt/entries/entry2/lowest_supported_fw_version b/plugins/uefi/tests/efi/esrt/entries/entry2/lowest_supported_fw_version new file mode 120000 index 000000000..9b389118c --- /dev/null +++ b/plugins/uefi/tests/efi/esrt/entries/entry2/lowest_supported_fw_version @@ -0,0 +1 @@ +../entry1/lowest_supported_fw_version \ No newline at end of file diff --git a/plugins/uefi/tests/tpm0/active b/plugins/uefi/tests/tpm0/active new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/plugins/uefi/tests/tpm0/active @@ -0,0 +1 @@ +1 diff --git a/plugins/uefi/tests/tpm0/caps b/plugins/uefi/tests/tpm0/caps new file mode 100644 index 000000000..af0d3cb00 --- /dev/null +++ b/plugins/uefi/tests/tpm0/caps @@ -0,0 +1,3 @@ +Manufacturer: 0x49465800 +TCG version: 1.2 +Firmware version: 6.40 diff --git a/plugins/uefi/tests/tpm0/enabled b/plugins/uefi/tests/tpm0/enabled new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/plugins/uefi/tests/tpm0/enabled @@ -0,0 +1 @@ +1 diff --git a/plugins/uefi/tests/tpm0/owned b/plugins/uefi/tests/tpm0/owned new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/plugins/uefi/tests/tpm0/owned @@ -0,0 +1 @@ +1 diff --git a/plugins/uefi/tests/tpm0/pcrs b/plugins/uefi/tests/tpm0/pcrs new file mode 100644 index 000000000..004e69048 --- /dev/null +++ b/plugins/uefi/tests/tpm0/pcrs @@ -0,0 +1,24 @@ +PCR-00: 3C 97 99 20 C9 00 99 60 09 27 D5 DA B3 81 EB 95 1E 7F C8 68 +PCR-01: CE 9F A4 B2 01 09 D8 81 14 EA 1A 6D 13 94 CD 45 5F 52 69 23 +PCR-02: 47 09 7A 9A AD C3 26 A4 93 91 26 63 A1 6F DF 53 D7 88 96 8E +PCR-03: B2 A8 3B 0E BF 2F 83 74 29 9A 5B 2B DF C3 1E A9 55 AD 72 36 +PCR-04: A4 A5 87 4C 59 94 8D 9B 93 66 0A F4 19 D8 6F F8 94 36 20 CC +PCR-05: 00 0B 58 00 89 72 EF 6C 2A AC 79 33 C4 AE 67 6B A6 EF CF 6A +PCR-06: B2 A8 3B 0E BF 2F 83 74 29 9A 5B 2B DF C3 1E A9 55 AD 72 36 +PCR-07: 0A 2A 68 15 85 0D AC B2 D1 F4 E0 C1 F4 56 D5 E2 81 08 6D EA +PCR-08: DB A7 29 4E 49 BA D7 9E 53 99 0A 6E 3A CB 52 97 B9 08 3A 66 +PCR-09: 19 F9 6F 10 83 F5 5B 50 98 26 C3 14 73 43 35 21 1F E6 39 E9 +PCR-10: 37 3D 89 9E 10 0D DD 2D 21 B5 F4 96 8D 4F DC A7 6D 1A C7 BD +PCR-11: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-14: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-15: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-17: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-18: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-19: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-20: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-21: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-22: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-23: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 diff --git a/plugins/uefi/uefi.quirk b/plugins/uefi/uefi.quirk new file mode 100644 index 000000000..a9719fef8 --- /dev/null +++ b/plugins/uefi/uefi.quirk @@ -0,0 +1,14 @@ +# Lenovo ThinkStation P920/P720 +[Guid=cc850e7a-6713-4fe3-8144-321a9e485ae0] +VersionFormat = intel-me +Name = Intel Management Engine + +# Lenovo ThinkStation P520/P520c +[Guid=e3af51e5-5afb-4b48-b5cb-13bf26becaab] +VersionFormat = intel-me +Name = Intel Management Engine + +# Lenovo ThinkStation P330/P330 Tiny & ThinkCentre M920X/M920q +[Guid=5b92717b-2cad-4a96-a13b-9d65781df8bf] +VersionFormat = intel-me2 +Name = Intel Management Engine diff --git a/plugins/unifying/README.md b/plugins/unifying/README.md index 94c255d13..c4e913a30 100644 --- a/plugins/unifying/README.md +++ b/plugins/unifying/README.md @@ -16,6 +16,31 @@ supplied by Logitech. Additional constants were taken from the Solaar[2] project. +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +a vendor-specific format that appears to be a subset of the Intel HEX format. + +This plugin supports the following protocol IDs: + + * com.logitech.unifying + * com.logitech.unifyingsigned + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values when in DFU mode: + + * `USB\VID_046D&PID_AAAA&REV_0001` + * `USB\VID_046D&PID_AAAA` + * `USB\VID_046D` + +When in runtime mode, the HID raw DeviceInstanceId values are used: + + * `HIDRAW\VEN_046D&DEV_C52B` + * `HIDRAW\VEN_046D` + Design Notes ------------ diff --git a/plugins/unifying/fu-plugin-unifying.c b/plugins/unifying/fu-plugin-unifying.c index 727bd4749..58af46f35 100644 --- a/plugins/unifying/fu-plugin-unifying.c +++ b/plugins/unifying/fu-plugin-unifying.c @@ -173,5 +173,9 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.logitech.unifying"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.logitech.unifyingsigned"); fu_plugin_add_udev_subsystem (plugin, "hidraw"); } diff --git a/plugins/unifying/fu-unifying-bootloader.c b/plugins/unifying/fu-unifying-bootloader.c index 3ffffe700..3e148d8c2 100644 --- a/plugins/unifying/fu-unifying-bootloader.c +++ b/plugins/unifying/fu-unifying-bootloader.c @@ -299,7 +299,10 @@ fu_unifying_bootloader_request (FuUnifyingBootloader *self, memcpy (buf_request + 0x04, req->data, 28); /* send request */ - fu_unifying_dump_raw ("host->device", buf_request, sizeof (buf_request)); + if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "host->device", + buf_request, sizeof (buf_request)); + } if (usb_device != NULL) { if (!g_usb_device_control_transfer (usb_device, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, @@ -332,7 +335,10 @@ fu_unifying_bootloader_request (FuUnifyingBootloader *self, &error_ignore)) { g_debug ("ignoring: %s", error_ignore->message); } else { - fu_unifying_dump_raw ("device->host", buf_response, actual_length); + if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "device->host", + buf_response, actual_length); + } } return TRUE; } @@ -365,7 +371,10 @@ fu_unifying_bootloader_request (FuUnifyingBootloader *self, } actual_length = sizeof (buf_response); } - fu_unifying_dump_raw ("device->host", buf_response, actual_length); + if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "device->host", + buf_response, actual_length); + } /* parse response */ if ((buf_response[0x00] & 0xf0) != req->cmd) { diff --git a/plugins/unifying/fu-unifying-common.c b/plugins/unifying/fu-unifying-common.c index 75e28ba77..b25b4ca2f 100644 --- a/plugins/unifying/fu-unifying-common.c +++ b/plugins/unifying/fu-unifying-common.c @@ -33,26 +33,6 @@ fu_unifying_buffer_read_uint16 (const gchar *str) return tmp; } -void -fu_unifying_dump_raw (const gchar *title, const guint8 *data, gsize len) -{ - g_autoptr(GString) str = g_string_new (NULL); - if (len == 0) - return; - if (g_getenv ("FWUPD_UNIFYING_VERBOSE") == NULL) - return; - - g_string_append_printf (str, "%s:", title); - for (gsize i = strlen (title); i < 16; i++) - g_string_append (str, " "); - for (gsize i = 0; i < len; i++) { - g_string_append_printf (str, "%02x ", data[i]); - if (i > 0 && i % 32 == 0) - g_string_append (str, "\n"); - } - g_debug ("%s", str->str); -} - gchar * fu_unifying_format_version (const gchar *name, guint8 major, guint8 minor, guint16 build) { @@ -65,152 +45,3 @@ fu_unifying_format_version (const gchar *name, guint8 major, guint8 minor, guint g_string_append_printf (str, "%02x.%02x_B%04x", major, minor, build); return g_string_free (str, FALSE); } - -static gboolean -fu_unifying_nonblock_flush (gint fd, GError **error) -{ - GPollFD poll[] = { - { - .fd = fd, - .events = G_IO_IN | G_IO_OUT | G_IO_ERR, - }, - }; - while (g_poll (poll, G_N_ELEMENTS(poll), 0) > 0) { - gchar c; - gint r = read (fd, &c, 1); - if (r < 0 && errno != EINTR) - break; - } - return TRUE; -} - -gboolean -fu_unifying_nonblock_write (gint fd, - const guint8 *data, - gsize data_sz, - GError **error) -{ - gssize wrote; - - /* sanity check */ - if (fd == 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write: fd is not open"); - return FALSE; - } - - /* flush pending reads */ - if (!fu_unifying_nonblock_flush (fd, error)) - return FALSE; - - /* write */ - wrote = write (fd, data, data_sz); - if (wrote != (gssize) data_sz) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write: " - "wrote %" G_GSSIZE_FORMAT " of %" G_GSIZE_FORMAT, - wrote, data_sz); - return FALSE; - } - return TRUE; -} - -gboolean -fu_unifying_nonblock_read (gint fd, - guint8 *data, - gsize data_sz, - gsize *data_len, - guint timeout, - GError **error) -{ - gssize len = 0; - gint64 ts_start; - GPollFD poll[] = { - { - .fd = fd, - .events = G_IO_IN | G_IO_OUT | G_IO_ERR, - }, - }; - - /* sanity check */ - if (fd == 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to read: fd is not open"); - return FALSE; - } - - /* do a read with a timeout */ - ts_start = g_get_monotonic_time (); - while (1) { - gint rc; - gint ts_remain = ((g_get_monotonic_time () - ts_start) / 1000) + timeout; - if (ts_remain <= 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_TIMED_OUT, - "timeout already passed"); - return FALSE; - } - - /* waits for the fd to become ready */ - rc = g_poll (poll, G_N_ELEMENTS (poll), ts_remain); - if (rc < 0) { - if (errno == EINTR) - continue; - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "read interrupted: %s", - g_strerror (errno)); - return FALSE; - } else if (rc == 0) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_TIMED_OUT, - "timeout"); - return FALSE; - } - - /* read data from fd */ - len = read (fd, data, data_sz); - if (len <= 0) { - if (len == -1 && errno == EINTR) - continue; - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to read data: %s", - g_strerror (errno)); - return FALSE; - } - - /* success */ - break; - }; - - /* success */ - if (data_len != NULL) - *data_len = (gsize) len; - return TRUE; -} - -gint -fu_unifying_nonblock_open (const gchar *filename, GError **error) -{ - gint fd; - fd = open (filename, O_RDWR | O_NONBLOCK); - if (fd < 0) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to open %s", filename); - return -1; - } - return fd; -} diff --git a/plugins/unifying/fu-unifying-common.h b/plugins/unifying/fu-unifying-common.h index 3e07178b8..a4f898863 100644 --- a/plugins/unifying/fu-unifying-common.h +++ b/plugins/unifying/fu-unifying-common.h @@ -22,10 +22,6 @@ G_BEGIN_DECLS /* Signed firmware are very long to verify on the device */ #define FU_UNIFYING_DEVICE_TIMEOUT_MS 20000 -void fu_unifying_dump_raw (const gchar *title, - const guint8 *data, - gsize len); - guint8 fu_unifying_buffer_read_uint8 (const gchar *str); guint16 fu_unifying_buffer_read_uint16 (const gchar *str); @@ -34,19 +30,6 @@ gchar *fu_unifying_format_version (const gchar *name, guint8 minor, guint16 build); -gint fu_unifying_nonblock_open (const gchar *filename, - GError **error); -gboolean fu_unifying_nonblock_read (gint fd, - guint8 *data, - gsize data_sz, - gsize *data_len, - guint timeout, - GError **error); -gboolean fu_unifying_nonblock_write (gint fd, - const guint8 *data, - gsize data_sz, - GError **error); - G_END_DECLS #endif /* __FU_UNIFYING_COMMON_H */ diff --git a/plugins/unifying/fu-unifying-hidpp.c b/plugins/unifying/fu-unifying-hidpp.c index d397d37cb..cf9213480 100644 --- a/plugins/unifying/fu-unifying-hidpp.c +++ b/plugins/unifying/fu-unifying-hidpp.c @@ -6,6 +6,7 @@ #include "config.h" +#include "fu-common.h" #include "fu-unifying-common.h" #include "fu-unifying-hidpp.h" @@ -56,29 +57,28 @@ fu_unifying_hidpp_msg_to_string (FuUnifyingHidppMsg *msg) } gboolean -fu_unifying_hidpp_send (gint fd, +fu_unifying_hidpp_send (FuIOChannel *io_channel, FuUnifyingHidppMsg *msg, guint timeout, GError **error) { gsize len = fu_unifying_hidpp_msg_get_payload_length (msg); - g_return_val_if_fail (fd > 0, FALSE); - /* only for HID++2.0 */ if (msg->hidpp_version >= 2.f) msg->function_id |= FU_UNIFYING_HIDPP_MSG_SW_ID; - fu_unifying_dump_raw ("host->device", (guint8 *) msg, len); - /* detailed debugging */ if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { g_autofree gchar *str = fu_unifying_hidpp_msg_to_string (msg); + fu_common_dump_raw (G_LOG_DOMAIN, "host->device", (guint8 *) msg, len); g_print ("%s", str); } /* HID */ - if (!fu_unifying_nonblock_write (fd, (guint8 *) msg, len, error)) { + if (!fu_io_channel_write_raw (io_channel, (guint8 *) msg, len, 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT | + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, error)) { g_prefix_error (error, "failed to send: "); return FALSE; } @@ -88,27 +88,27 @@ fu_unifying_hidpp_send (gint fd, } gboolean -fu_unifying_hidpp_receive (gint fd, +fu_unifying_hidpp_receive (FuIOChannel *io_channel, FuUnifyingHidppMsg *msg, guint timeout, GError **error) { gsize read_size = 0; - g_return_val_if_fail (fd > 0, FALSE); - - if (!fu_unifying_nonblock_read (fd, - (guint8 *) msg, - sizeof(FuUnifyingHidppMsg), - &read_size, - timeout, - error)) { + if (!fu_io_channel_read_raw (io_channel, + (guint8 *) msg, + sizeof(FuUnifyingHidppMsg), + &read_size, + timeout, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error)) { g_prefix_error (error, "failed to receive: "); return FALSE; } /* check long enough, but allow returning oversize packets */ - fu_unifying_dump_raw ("device->host", (guint8 *) msg, read_size); + if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "device->host", (guint8 *) msg, read_size); if (read_size < fu_unifying_hidpp_msg_get_payload_length (msg)) { g_set_error (error, G_IO_ERROR, @@ -130,26 +130,24 @@ fu_unifying_hidpp_receive (gint fd, } gboolean -fu_unifying_hidpp_transfer (gint fd, FuUnifyingHidppMsg *msg, GError **error) +fu_unifying_hidpp_transfer (FuIOChannel *io_channel, FuUnifyingHidppMsg *msg, GError **error) { guint timeout = FU_UNIFYING_DEVICE_TIMEOUT_MS; guint ignore_cnt = 0; g_autoptr(FuUnifyingHidppMsg) msg_tmp = fu_unifying_hidpp_msg_new (); - g_return_val_if_fail (fd > 0, FALSE); - /* increase timeout for some operations */ if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) timeout *= 10; /* send request */ - if (!fu_unifying_hidpp_send (fd, msg, timeout, error)) + if (!fu_unifying_hidpp_send (io_channel, msg, timeout, error)) return FALSE; /* keep trying to receive until we get a valid reply */ while (1) { msg_tmp->hidpp_version = msg->hidpp_version; - if (!fu_unifying_hidpp_receive (fd, msg_tmp, timeout, error)) { + if (!fu_unifying_hidpp_receive (io_channel, msg_tmp, timeout, error)) { g_prefix_error (error, "failed to receive: "); return FALSE; } diff --git a/plugins/unifying/fu-unifying-hidpp.h b/plugins/unifying/fu-unifying-hidpp.h index 9a52d7e81..bcc05dd4f 100644 --- a/plugins/unifying/fu-unifying-hidpp.h +++ b/plugins/unifying/fu-unifying-hidpp.h @@ -9,6 +9,8 @@ #include +#include "fu-io-channel.h" + G_BEGIN_DECLS /* @@ -136,15 +138,15 @@ G_BEGIN_DECLS #include "fu-unifying-hidpp-msg.h" -gboolean fu_unifying_hidpp_send (gint fd, +gboolean fu_unifying_hidpp_send (FuIOChannel *self, FuUnifyingHidppMsg *msg, guint timeout, GError **error); -gboolean fu_unifying_hidpp_receive (gint fd, +gboolean fu_unifying_hidpp_receive (FuIOChannel *self, FuUnifyingHidppMsg *msg, guint timeout, GError **error); -gboolean fu_unifying_hidpp_transfer (gint fd, +gboolean fu_unifying_hidpp_transfer (FuIOChannel *self, FuUnifyingHidppMsg *msg, GError **error); diff --git a/plugins/unifying/fu-unifying-peripheral.c b/plugins/unifying/fu-unifying-peripheral.c index d6ef53642..b8253c615 100644 --- a/plugins/unifying/fu-unifying-peripheral.c +++ b/plugins/unifying/fu-unifying-peripheral.c @@ -7,7 +7,6 @@ #include "config.h" #include -#include #include "fu-unifying-common.h" #include "fu-unifying-peripheral.h" @@ -22,7 +21,7 @@ struct _FuUnifyingPeripheral guint8 hidpp_version; gboolean is_updatable; gboolean is_active; - gint udev_fd; + FuIOChannel *io_channel; GPtrArray *feature_index; /* of FuUnifyingHidppMap */ }; @@ -136,7 +135,7 @@ fu_unifying_peripheral_ping (FuUnifyingPeripheral *self, GError **error) msg->data[1] = 0x00; msg->data[2] = 0xaa; /* user-selected value */ msg->hidpp_version = self->hidpp_version; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, &error_local)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, &error_local)) { if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { @@ -181,11 +180,9 @@ static gboolean fu_unifying_peripheral_close (FuDevice *device, GError **error) { FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); - if (self->udev_fd > 0) { - if (!g_close (self->udev_fd, error)) - return FALSE; - self->udev_fd = 0; - } + if (!fu_io_channel_shutdown (self->io_channel, error)) + return FALSE; + g_clear_object (&self->io_channel); return TRUE; } @@ -206,7 +203,7 @@ fu_unifying_peripheral_poll (FuDevice *device, GError **error) /* flush pending data */ msg->device_id = self->hidpp_id; msg->hidpp_version = self->hidpp_version; - if (!fu_unifying_hidpp_receive (self->udev_fd, msg, timeout, &error_local)) { + if (!fu_unifying_hidpp_receive (self->io_channel, msg, timeout, &error_local)) { if (!g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { @@ -242,8 +239,8 @@ fu_unifying_peripheral_open (FuDevice *device, GError **error) const gchar *devpath = g_udev_device_get_device_file (udev_device); /* open */ - self->udev_fd = fu_unifying_nonblock_open (devpath, error); - if (self->udev_fd < 0) + self->io_channel = fu_io_channel_new_file (devpath, error); + if (self->io_channel == NULL) return FALSE; return TRUE; @@ -255,8 +252,6 @@ fu_unifying_peripheral_to_string (FuDevice *device, GString *str) FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); g_string_append_printf (str, " HidppVersion:\t\t%u\n", self->hidpp_version); - if (self->udev_fd > 0) - g_string_append_printf (str, " UdevDevice:\t\t%i\n", self->udev_fd); if (self->hidpp_id != HIDPP_DEVICE_ID_UNSET) g_string_append_printf (str, " HidppId:\t\t0x%02x\n", (guint) self->hidpp_id); if (self->battery_level != 0) @@ -301,7 +296,7 @@ fu_unifying_peripheral_fetch_firmware_info (FuUnifyingPeripheral *self, GError * msg->sub_id = idx; msg->function_id = 0x00 << 4; /* getCount */ msg->hidpp_version = self->hidpp_version; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to get firmware count: "); return FALSE; } @@ -319,7 +314,7 @@ fu_unifying_peripheral_fetch_firmware_info (FuUnifyingPeripheral *self, GError * msg->sub_id = idx; msg->function_id = 0x01 << 4; /* getInfo */ msg->data[0] = i; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to get firmware info: "); return FALSE; } @@ -371,7 +366,7 @@ fu_unifying_peripheral_fetch_battery_level (FuUnifyingPeripheral *self, GError * msg->sub_id = idx; msg->function_id = 0x00; /* GetBatteryLevelStatus */ msg->hidpp_version = self->hidpp_version; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to get battery info: "); return FALSE; } @@ -389,7 +384,7 @@ fu_unifying_peripheral_fetch_battery_level (FuUnifyingPeripheral *self, GError * msg->sub_id = HIDPP_SUBID_GET_REGISTER; msg->function_id = HIDPP_REGISTER_BATTERY_MILEAGE; msg->hidpp_version = self->hidpp_version; - if (fu_unifying_hidpp_transfer (self->udev_fd, msg, NULL)) { + if (fu_unifying_hidpp_transfer (self->io_channel, msg, NULL)) { if (msg->data[0] != 0x00) self->battery_level = msg->data[0]; return TRUE; @@ -397,7 +392,7 @@ fu_unifying_peripheral_fetch_battery_level (FuUnifyingPeripheral *self, GError * /* try HID++1.0 battery status instead */ msg->function_id = HIDPP_REGISTER_BATTERY_STATUS; - if (fu_unifying_hidpp_transfer (self->udev_fd, msg, NULL)) { + if (fu_unifying_hidpp_transfer (self->io_channel, msg, NULL)) { switch (msg->data[0]) { case 1: /* 0 - 10 */ self->battery_level = 5; @@ -440,7 +435,7 @@ fu_unifying_hidpp_feature_search (FuDevice *device, guint16 feature, GError **er msg->data[1] = feature; msg->data[2] = 0x00; msg->hidpp_version = self->hidpp_version; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to get idx for feature %s [0x%04x]: ", fu_unifying_hidpp_feature_to_string (feature), feature); @@ -551,7 +546,7 @@ fu_unifying_peripheral_setup (FuDevice *device, GError **error) msg->sub_id = idx; msg->function_id = 0x02 << 4; /* getDeviceType */ msg->hidpp_version = self->hidpp_version; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to get device type: "); return FALSE; } @@ -578,7 +573,7 @@ fu_unifying_peripheral_setup (FuDevice *device, GError **error) msg->sub_id = idx; msg->function_id = 0x00 << 4; /* getDfuStatus */ msg->hidpp_version = self->hidpp_version; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to get DFU status: "); return FALSE; } @@ -631,7 +626,7 @@ fu_unifying_peripheral_detach (FuDevice *device, GError **error) msg->hidpp_version = self->hidpp_version; msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to put device into DFU mode: "); return FALSE; } @@ -654,7 +649,7 @@ fu_unifying_peripheral_detach (FuDevice *device, GError **error) msg->data[5] = 'F'; msg->data[6] = 'U'; msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to put device into DFU mode: "); return FALSE; } @@ -833,7 +828,7 @@ fu_unifying_peripheral_write_firmware_pkt (FuUnifyingPeripheral *self, msg->function_id = cmd << 4; /* dfuStart or dfuCmdDataX */ msg->hidpp_version = self->hidpp_version; memcpy (msg->data, data, 16); - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, &error_local)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, &error_local)) { g_prefix_error (error, "failed to supply program data: "); return FALSE; } @@ -860,7 +855,7 @@ fu_unifying_peripheral_write_firmware_pkt (FuUnifyingPeripheral *self, for (guint retry = 0; retry < 10; retry++) { g_autoptr(FuUnifyingHidppMsg) msg2 = fu_unifying_hidpp_msg_new (); msg2->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID; - if (!fu_unifying_hidpp_receive (self->udev_fd, msg2, 15000, error)) + if (!fu_unifying_hidpp_receive (self->io_channel, msg2, 15000, error)) return FALSE; if (fu_unifying_hidpp_msg_is_reply (msg, msg2)) { g_autoptr(GError) error2 = NULL; @@ -956,7 +951,7 @@ fu_unifying_peripheral_attach (FuDevice *device, GError **error) msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID | // inferred? FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to restart device: "); return FALSE; } diff --git a/plugins/unifying/fu-unifying-runtime.c b/plugins/unifying/fu-unifying-runtime.c index 622d8523b..7ba1eac4f 100644 --- a/plugins/unifying/fu-unifying-runtime.c +++ b/plugins/unifying/fu-unifying-runtime.c @@ -7,7 +7,6 @@ #include "config.h" #include -#include #include "fu-unifying-common.h" #include "fu-unifying-runtime.h" @@ -18,7 +17,7 @@ struct _FuUnifyingRuntime FuUdevDevice parent_instance; guint8 version_bl_major; gboolean signed_firmware; - gint udev_fd; + FuIOChannel *io_channel; }; G_DEFINE_TYPE (FuUnifyingRuntime, fu_unifying_runtime, FU_TYPE_UDEV_DEVICE) @@ -31,9 +30,6 @@ static void fu_unifying_runtime_to_string (FuDevice *device, GString *str) { FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); - - if (self->udev_fd > 0) - g_string_append_printf (str, " UdevDevice:\t\t%i\n", self->udev_fd); g_string_append_printf (str, " SignedFirmware:\t%i\n", self->signed_firmware); } @@ -49,18 +45,16 @@ fu_unifying_runtime_enable_notifications (FuUnifyingRuntime *self, GError **erro msg->data[1] = 0x05; /* Wireless + SoftwarePresent */ msg->data[2] = 0x00; msg->hidpp_version = 1; - return fu_unifying_hidpp_transfer (self->udev_fd, msg, error); + return fu_unifying_hidpp_transfer (self->io_channel, msg, error); } static gboolean fu_unifying_runtime_close (FuDevice *device, GError **error) { FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); - if (self->udev_fd > 0) { - if (!g_close (self->udev_fd, error)) - return FALSE; - self->udev_fd = 0; - } + if (!fu_io_channel_shutdown (self->io_channel, error)) + return FALSE; + g_clear_object (&self->io_channel); return TRUE; } @@ -80,7 +74,7 @@ fu_unifying_runtime_poll (FuDevice *device, GError **error) /* is there any pending data to read */ msg->hidpp_version = 1; - if (!fu_unifying_hidpp_receive (self->udev_fd, msg, timeout, &error_local)) { + if (!fu_unifying_hidpp_receive (self->io_channel, msg, timeout, &error_local)) { if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { @@ -126,8 +120,8 @@ fu_unifying_runtime_open (FuDevice *device, GError **error) const gchar *devpath = g_udev_device_get_device_file (udev_device); /* open, but don't block */ - self->udev_fd = fu_unifying_nonblock_open (devpath, error); - if (self->udev_fd < 0) + self->io_channel = fu_io_channel_new_file (devpath, error); + if (self->io_channel == NULL) return FALSE; /* poll for notifications */ @@ -208,7 +202,7 @@ fu_unifying_runtime_setup_internal (FuDevice *device, GError **error) msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION; msg->data[0] = i; msg->hidpp_version = 1; - if (!fu_unifying_hidpp_transfer (self->udev_fd, msg, error)) { + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { g_prefix_error (error, "failed to read device config: "); return FALSE; } @@ -286,7 +280,7 @@ fu_unifying_runtime_detach (FuDevice *device, GError **error) msg->data[2] = 'P'; msg->hidpp_version = 1; msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; - if (!fu_unifying_hidpp_send (self->udev_fd, msg, FU_UNIFYING_DEVICE_TIMEOUT_MS, error)) { + if (!fu_unifying_hidpp_send (self->io_channel, msg, FU_UNIFYING_DEVICE_TIMEOUT_MS, error)) { g_prefix_error (error, "failed to detach to bootloader: "); return FALSE; } diff --git a/plugins/unifying/meson.build b/plugins/unifying/meson.build index 011fd0187..7e04912db 100644 --- a/plugins/unifying/meson.build +++ b/plugins/unifying/meson.build @@ -8,6 +8,7 @@ install_data([ shared_module('fu_plugin_unifying', + fu_hash, sources : [ 'fu-plugin-unifying.c', 'fu-unifying-bootloader.c', @@ -26,6 +27,9 @@ shared_module('fu_plugin_unifying', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, @@ -47,7 +51,7 @@ if get_option('tests') plugin_deps, ], link_with : [ - fwupd, + libfwupdprivate, ], c_args : cargs, ) diff --git a/plugins/unifying/unifying.quirk b/plugins/unifying/unifying.quirk index b135c990c..2191bb080 100644 --- a/plugins/unifying/unifying.quirk +++ b/plugins/unifying/unifying.quirk @@ -3,6 +3,7 @@ Plugin = unifying Flags = is-receiver VendorId=USB:0x046D +InstallDuration = 7 # Nordic [DeviceInstanceId=USB\VID_046D&PID_AAAA] @@ -10,6 +11,7 @@ Plugin = unifying Flags = is-bootloader,is-nordic FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 7 # Nordic Pico [DeviceInstanceId=USB\VID_046D&PID_AAAE] @@ -17,6 +19,7 @@ Plugin = unifying Flags = is-bootloader,is-nordic FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 7 # Texas [DeviceInstanceId=USB\VID_046D&PID_AAAC] @@ -24,6 +27,7 @@ Plugin = unifying Flags = is-bootloader,is-texas FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 7 # Texas Pico [DeviceInstanceId=USB\VID_046D&PID_AAAD] @@ -31,3 +35,4 @@ Plugin = unifying Flags = is-bootloader,is-texas FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 7 diff --git a/plugins/upower/fu-plugin-upower.c b/plugins/upower/fu-plugin-upower.c index c68adb007..3df98993f 100644 --- a/plugins/upower/fu-plugin-upower.c +++ b/plugins/upower/fu-plugin-upower.c @@ -8,13 +8,17 @@ #include "fu-plugin-vfuncs.h" +#define MINIMUM_BATTERY_PERCENTAGE 30 + struct FuPluginData { - GDBusProxy *proxy; + GDBusProxy *upower_proxy; + GDBusProxy *display_proxy; }; void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); } @@ -22,15 +26,17 @@ void fu_plugin_destroy (FuPlugin *plugin) { FuPluginData *data = fu_plugin_get_data (plugin); - if (data->proxy != NULL) - g_object_unref (data->proxy); + if (data->upower_proxy != NULL) + g_object_unref (data->upower_proxy); + if (data->display_proxy != NULL) + g_object_unref (data->display_proxy); } gboolean fu_plugin_startup (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); - data->proxy = + data->upower_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, @@ -39,31 +45,87 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) "org.freedesktop.UPower", NULL, error); - if (data->proxy == NULL) { + if (data->upower_proxy == NULL) { g_prefix_error (error, "failed to connect to upower: "); return FALSE; } + data->display_proxy = + g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + "org.freedesktop.UPower", + "/org/freedesktop/UPower/devices/DisplayDevice", + "org.freedesktop.UPower.Device", + NULL, + error); + if (data->display_proxy == NULL) { + g_prefix_error (error, "failed to connect to upower: "); + return FALSE; + } + return TRUE; } +static gboolean +fu_plugin_upower_check_percentage_level (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + gdouble level; + guint power_type; + g_autoptr(GVariant) percentage_val = NULL; + g_autoptr(GVariant) type_val = NULL; + + /* check that we "have" a battery */ + type_val = g_dbus_proxy_get_cached_property (data->display_proxy, "Type"); + if (type_val == NULL) { + g_warning ("Failed to query power type, assume AC power"); + return TRUE; + } + power_type = g_variant_get_uint32 (type_val); + if (power_type != 2) { + g_debug ("Not running on battery (Type: %u)", power_type); + return TRUE; + } + + /* check percentage high enough */ + percentage_val = g_dbus_proxy_get_cached_property (data->display_proxy, "Percentage"); + if (percentage_val == NULL) { + g_warning ("Failed to query power percentage level, assume enough charge"); + return TRUE; + } + level = g_variant_get_double (percentage_val); + g_debug ("System power source is %.1f%%", level); + + return level >= MINIMUM_BATTERY_PERCENTAGE; +} + +static gboolean +fu_plugin_upower_check_on_battery (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autoptr(GVariant) value = NULL; + + value = g_dbus_proxy_get_cached_property (data->upower_proxy, "OnBattery"); + if (value == NULL) { + g_warning ("failed to get OnBattery value, assume on AC power"); + return FALSE; + } + return g_variant_get_boolean (value); +} + gboolean fu_plugin_update_prepare (FuPlugin *plugin, FwupdInstallFlags flags, FuDevice *device, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - g_autoptr(GVariant) value = NULL; - - /* can we only do this on AC power */ + /* not all devices need this */ if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC)) return TRUE; - value = g_dbus_proxy_get_cached_property (data->proxy, "OnBattery"); - if (value == NULL) { - g_warning ("failed to get OnBattery value, assume on AC power"); - return TRUE; - } - if (g_variant_get_boolean (value) && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + + /* determine if operating on AC or battery */ + if (fu_plugin_upower_check_on_battery (plugin) && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_AC_POWER_REQUIRED, @@ -71,5 +133,17 @@ fu_plugin_update_prepare (FuPlugin *plugin, "when not on AC power unless forced"); return FALSE; } + + /* deteremine if battery high enough */ + if (!fu_plugin_upower_check_percentage_level (plugin) && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_AC_POWER_REQUIRED, + "Cannot install update when battery " + "is not at least %d%% unless forced", + MINIMUM_BATTERY_PERCENTAGE); + return FALSE; + } return TRUE; } diff --git a/plugins/upower/meson.build b/plugins/upower/meson.build index 9d027744b..0f9e0dc3a 100644 --- a/plugins/upower/meson.build +++ b/plugins/upower/meson.build @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginUpower"'] shared_module('fu_plugin_upower', + fu_hash, sources : [ 'fu-plugin-upower.c', ], @@ -11,6 +12,9 @@ shared_module('fu_plugin_upower', ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff --git a/plugins/wacom-raw/README.md b/plugins/wacom-raw/README.md new file mode 100644 index 000000000..2d5f3890a --- /dev/null +++ b/plugins/wacom-raw/README.md @@ -0,0 +1,36 @@ +Wacom RAW Support +================= + +Introduction +------------ + +This plugin updates integrated Wacom AES and EMR devices. They are typically +connected using I²C and not USB. + +GUID Generation +--------------- + +The HID DeviceInstanceId values are used, e.g. `HIDRAW\VEN_056A&DEV_4875`. + +Additionally, for supported AES devices an extra GUID is added for the hardware +ID (e.g. `WACOM\HWID_%04X`) to further disambiguate the panels. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +Intel HEX file format. + +This plugin supports the following protocol ID: + + * com.wacom.raw + +Quirk use +--------- +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|-------------------------|-------------------------------------|-----------------------| +| `WacomI2cFlashBlockSize`| Block size to transfer firmware | 1.2.4 | +| `WacomI2cFlashBaseAddr` | Base address for firmware | 1.2.4 | +| `WacomI2cFlashSize` | Maximum size of the firmware zone | 1.2.4 | diff --git a/plugins/wacom-raw/data/hid-recorder.txt b/plugins/wacom-raw/data/hid-recorder.txt new file mode 100644 index 000000000..a76b65db5 --- /dev/null +++ b/plugins/wacom-raw/data/hid-recorder.txt @@ -0,0 +1,470 @@ +# WCOM4875:00 056A:4875 +# 0x05, 0x0d, // Usage Page (Digitizers) 0 +# 0x09, 0x04, // Usage (Touch Screen) 2 +# 0xa1, 0x01, // Collection (Application) 4 +# 0x85, 0x0c, // Report ID (12) 6 +# 0x95, 0x01, // Report Count (1) 8 +# 0x75, 0x08, // Report Size (8) 10 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 12 +# 0x15, 0x00, // Logical Minimum (0) 15 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 17 +# 0x09, 0x54, // Usage (Contact Count) 19 +# 0x81, 0x02, // Input (Data,Var,Abs) 21 +# 0x05, 0x0d, // Usage Page (Digitizers) 23 +# 0x09, 0x22, // Usage (Finger) 25 +# 0xa1, 0x02, // Collection (Logical) 27 +# 0x09, 0x42, // Usage (Tip Switch) 29 +# 0x15, 0x00, // Logical Minimum (0) 31 +# 0x25, 0x01, // Logical Maximum (1) 33 +# 0x75, 0x01, // Report Size (1) 35 +# 0x95, 0x01, // Report Count (1) 37 +# 0x81, 0x02, // Input (Data,Var,Abs) 39 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 41 +# 0x09, 0x47, // Usage (Confidence) 43 +# 0x81, 0x02, // Input (Data,Var,Abs) 45 +# 0x95, 0x05, // Report Count (5) 47 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 49 +# 0x75, 0x10, // Report Size (16) 51 +# 0x09, 0x51, // Usage (Contact Id) 53 +# 0x95, 0x01, // Report Count (1) 55 +# 0x81, 0x02, // Input (Data,Var,Abs) 57 +# 0x05, 0x01, // Usage Page (Generic Desktop) 59 +# 0x75, 0x10, // Report Size (16) 61 +# 0x95, 0x01, // Report Count (1) 63 +# 0x55, 0x0e, // Unit Exponent (-2) 65 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 67 +# 0x09, 0x30, // Usage (X) 69 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 71 +# 0x35, 0x00, // Physical Minimum (0) 74 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 76 +# 0x81, 0x02, // Input (Data,Var,Abs) 79 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 81 +# 0x09, 0x31, // Usage (Y) 84 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 86 +# 0x81, 0x02, // Input (Data,Var,Abs) 89 +# 0xc0, // End Collection 91 +# 0x05, 0x0d, // Usage Page (Digitizers) 92 +# 0x09, 0x22, // Usage (Finger) 94 +# 0xa1, 0x02, // Collection (Logical) 96 +# 0x09, 0x42, // Usage (Tip Switch) 98 +# 0x15, 0x00, // Logical Minimum (0) 100 +# 0x25, 0x01, // Logical Maximum (1) 102 +# 0x75, 0x01, // Report Size (1) 104 +# 0x95, 0x01, // Report Count (1) 106 +# 0x81, 0x02, // Input (Data,Var,Abs) 108 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 110 +# 0x09, 0x47, // Usage (Confidence) 112 +# 0x81, 0x02, // Input (Data,Var,Abs) 114 +# 0x95, 0x05, // Report Count (5) 116 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 118 +# 0x75, 0x10, // Report Size (16) 120 +# 0x09, 0x51, // Usage (Contact Id) 122 +# 0x95, 0x01, // Report Count (1) 124 +# 0x81, 0x02, // Input (Data,Var,Abs) 126 +# 0x05, 0x01, // Usage Page (Generic Desktop) 128 +# 0x75, 0x10, // Report Size (16) 130 +# 0x95, 0x01, // Report Count (1) 132 +# 0x55, 0x0e, // Unit Exponent (-2) 134 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 136 +# 0x09, 0x30, // Usage (X) 138 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 140 +# 0x35, 0x00, // Physical Minimum (0) 143 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 145 +# 0x81, 0x02, // Input (Data,Var,Abs) 148 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 150 +# 0x09, 0x31, // Usage (Y) 153 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 155 +# 0x81, 0x02, // Input (Data,Var,Abs) 158 +# 0xc0, // End Collection 160 +# 0x05, 0x0d, // Usage Page (Digitizers) 161 +# 0x09, 0x22, // Usage (Finger) 163 +# 0xa1, 0x02, // Collection (Logical) 165 +# 0x09, 0x42, // Usage (Tip Switch) 167 +# 0x15, 0x00, // Logical Minimum (0) 169 +# 0x25, 0x01, // Logical Maximum (1) 171 +# 0x75, 0x01, // Report Size (1) 173 +# 0x95, 0x01, // Report Count (1) 175 +# 0x81, 0x02, // Input (Data,Var,Abs) 177 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 179 +# 0x09, 0x47, // Usage (Confidence) 181 +# 0x81, 0x02, // Input (Data,Var,Abs) 183 +# 0x95, 0x05, // Report Count (5) 185 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 187 +# 0x75, 0x10, // Report Size (16) 189 +# 0x09, 0x51, // Usage (Contact Id) 191 +# 0x95, 0x01, // Report Count (1) 193 +# 0x81, 0x02, // Input (Data,Var,Abs) 195 +# 0x05, 0x01, // Usage Page (Generic Desktop) 197 +# 0x75, 0x10, // Report Size (16) 199 +# 0x95, 0x01, // Report Count (1) 201 +# 0x55, 0x0e, // Unit Exponent (-2) 203 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 205 +# 0x09, 0x30, // Usage (X) 207 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 209 +# 0x35, 0x00, // Physical Minimum (0) 212 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 214 +# 0x81, 0x02, // Input (Data,Var,Abs) 217 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 219 +# 0x09, 0x31, // Usage (Y) 222 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 224 +# 0x81, 0x02, // Input (Data,Var,Abs) 227 +# 0xc0, // End Collection 229 +# 0x05, 0x0d, // Usage Page (Digitizers) 230 +# 0x09, 0x22, // Usage (Finger) 232 +# 0xa1, 0x02, // Collection (Logical) 234 +# 0x09, 0x42, // Usage (Tip Switch) 236 +# 0x15, 0x00, // Logical Minimum (0) 238 +# 0x25, 0x01, // Logical Maximum (1) 240 +# 0x75, 0x01, // Report Size (1) 242 +# 0x95, 0x01, // Report Count (1) 244 +# 0x81, 0x02, // Input (Data,Var,Abs) 246 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 248 +# 0x09, 0x47, // Usage (Confidence) 250 +# 0x81, 0x02, // Input (Data,Var,Abs) 252 +# 0x95, 0x05, // Report Count (5) 254 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 256 +# 0x75, 0x10, // Report Size (16) 258 +# 0x09, 0x51, // Usage (Contact Id) 260 +# 0x95, 0x01, // Report Count (1) 262 +# 0x81, 0x02, // Input (Data,Var,Abs) 264 +# 0x05, 0x01, // Usage Page (Generic Desktop) 266 +# 0x75, 0x10, // Report Size (16) 268 +# 0x95, 0x01, // Report Count (1) 270 +# 0x55, 0x0e, // Unit Exponent (-2) 272 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 274 +# 0x09, 0x30, // Usage (X) 276 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 278 +# 0x35, 0x00, // Physical Minimum (0) 281 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 283 +# 0x81, 0x02, // Input (Data,Var,Abs) 286 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 288 +# 0x09, 0x31, // Usage (Y) 291 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 293 +# 0x81, 0x02, // Input (Data,Var,Abs) 296 +# 0xc0, // End Collection 298 +# 0x05, 0x0d, // Usage Page (Digitizers) 299 +# 0x09, 0x22, // Usage (Finger) 301 +# 0xa1, 0x02, // Collection (Logical) 303 +# 0x09, 0x42, // Usage (Tip Switch) 305 +# 0x15, 0x00, // Logical Minimum (0) 307 +# 0x25, 0x01, // Logical Maximum (1) 309 +# 0x75, 0x01, // Report Size (1) 311 +# 0x95, 0x01, // Report Count (1) 313 +# 0x81, 0x02, // Input (Data,Var,Abs) 315 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 317 +# 0x09, 0x47, // Usage (Confidence) 319 +# 0x81, 0x02, // Input (Data,Var,Abs) 321 +# 0x95, 0x05, // Report Count (5) 323 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 325 +# 0x75, 0x10, // Report Size (16) 327 +# 0x09, 0x51, // Usage (Contact Id) 329 +# 0x95, 0x01, // Report Count (1) 331 +# 0x81, 0x02, // Input (Data,Var,Abs) 333 +# 0x05, 0x01, // Usage Page (Generic Desktop) 335 +# 0x75, 0x10, // Report Size (16) 337 +# 0x95, 0x01, // Report Count (1) 339 +# 0x55, 0x0e, // Unit Exponent (-2) 341 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 343 +# 0x09, 0x30, // Usage (X) 345 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 347 +# 0x35, 0x00, // Physical Minimum (0) 350 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 352 +# 0x81, 0x02, // Input (Data,Var,Abs) 355 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 357 +# 0x09, 0x31, // Usage (Y) 360 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 362 +# 0x81, 0x02, // Input (Data,Var,Abs) 365 +# 0xc0, // End Collection 367 +# 0x05, 0x0d, // Usage Page (Digitizers) 368 +# 0x27, 0xff, 0xff, 0x00, 0x00, // Logical Maximum (65535) 370 +# 0x75, 0x10, // Report Size (16) 375 +# 0x95, 0x01, // Report Count (1) 377 +# 0x09, 0x56, // Usage (Scan Time) 379 +# 0x81, 0x02, // Input (Data,Var,Abs) 381 +# 0x85, 0x0c, // Report ID (12) 383 +# 0x09, 0x55, // Usage (Contact Max) 385 +# 0x75, 0x08, // Report Size (8) 387 +# 0x95, 0x01, // Report Count (1) 389 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 391 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 394 +# 0x85, 0x0a, // Report ID (10) 396 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 398 +# 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 401 +# 0x96, 0x00, 0x01, // Report Count (256) 403 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 406 +# 0xc0, // End Collection 408 +# 0x06, 0x11, 0xff, // Usage Page (Vendor Usage Page 0xff11) 409 +# 0x09, 0x11, // Usage (Vendor Usage 0x11) 412 +# 0xa1, 0x01, // Collection (Application) 414 +# 0x85, 0x03, // Report ID (3) 416 +# 0xa1, 0x02, // Collection (Logical) 418 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 420 +# 0x75, 0x08, // Report Size (8) 422 +# 0x15, 0x00, // Logical Minimum (0) 424 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 426 +# 0x95, 0x27, // Report Count (39) 429 +# 0x81, 0x02, // Input (Data,Var,Abs) 431 +# 0xc0, // End Collection 433 +# 0x85, 0x02, // Report ID (2) 434 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 436 +# 0x95, 0x01, // Report Count (1) 438 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 440 +# 0x85, 0x03, // Report ID (3) 442 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 444 +# 0x95, 0x3f, // Report Count (63) 446 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 448 +# 0x85, 0x04, // Report ID (4) 450 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 452 +# 0x95, 0x0f, // Report Count (15) 454 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 456 +# 0x85, 0x07, // Report ID (7) 458 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 460 +# 0x96, 0x00, 0x01, // Report Count (256) 462 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 465 +# 0x85, 0x08, // Report ID (8) 467 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 469 +# 0x96, 0x87, 0x00, // Report Count (135) 471 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 474 +# 0x85, 0x09, // Report ID (9) 476 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 478 +# 0x96, 0x3f, 0x00, // Report Count (63) 480 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 483 +# 0x85, 0x0d, // Report ID (13) 485 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 487 +# 0x95, 0x07, // Report Count (7) 489 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 491 +# 0xc0, // End Collection 493 +# 0x05, 0x0d, // Usage Page (Digitizers) 494 +# 0x09, 0x0e, // Usage (Device Configuration) 496 +# 0xa1, 0x01, // Collection (Application) 498 +# 0x85, 0x0e, // Report ID (14) 500 +# 0x09, 0x23, // Usage (Device Settings) 502 +# 0xa1, 0x02, // Collection (Logical) 504 +# 0x09, 0x52, // Usage (Inputmode) 506 +# 0x09, 0x53, // Usage (Device Index) 508 +# 0x15, 0x00, // Logical Minimum (0) 510 +# 0x25, 0x0a, // Logical Maximum (10) 512 +# 0x75, 0x08, // Report Size (8) 514 +# 0x95, 0x02, // Report Count (2) 516 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 518 +# 0xc0, // End Collection 520 +# 0xc0, // End Collection 521 +# 0x05, 0x0d, // Usage Page (Digitizers) 522 +# 0x09, 0x02, // Usage (Pen) 524 +# 0xa1, 0x01, // Collection (Application) 526 +# 0x85, 0x06, // Report ID (6) 528 +# 0xa4, // Push 530 +# 0x09, 0x20, // Usage (Stylus) 531 +# 0xa1, 0x00, // Collection (Physical) 533 +# 0x09, 0x42, // Usage (Tip Switch) 535 +# 0x09, 0x44, // Usage (Barrel Switch) 537 +# 0x09, 0x45, // Usage (Eraser) 539 +# 0x09, 0x3c, // Usage (Invert) 541 +# 0x09, 0x5a, // Usage (Secondary Barrel Switch) 543 +# 0x09, 0x32, // Usage (In Range) 545 +# 0x15, 0x00, // Logical Minimum (0) 547 +# 0x25, 0x01, // Logical Maximum (1) 549 +# 0x75, 0x01, // Report Size (1) 551 +# 0x95, 0x06, // Report Count (6) 553 +# 0x81, 0x02, // Input (Data,Var,Abs) 555 +# 0x95, 0x02, // Report Count (2) 557 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 559 +# 0x05, 0x01, // Usage Page (Generic Desktop) 561 +# 0x09, 0x30, // Usage (X) 563 +# 0x27, 0x70, 0x86, 0x00, 0x00, // Logical Maximum (34416) 565 +# 0x47, 0x70, 0x86, 0x00, 0x00, // Physical Maximum (34416) 570 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 575 +# 0x55, 0x0d, // Unit Exponent (-3) 577 +# 0x75, 0x10, // Report Size (16) 579 +# 0x95, 0x01, // Report Count (1) 581 +# 0x81, 0x02, // Input (Data,Var,Abs) 583 +# 0x09, 0x31, // Usage (Y) 585 +# 0x27, 0x9f, 0x4b, 0x00, 0x00, // Logical Maximum (19359) 587 +# 0x47, 0x9f, 0x4b, 0x00, 0x00, // Physical Maximum (19359) 592 +# 0x81, 0x02, // Input (Data,Var,Abs) 597 +# 0x45, 0x00, // Physical Maximum (0) 599 +# 0x65, 0x00, // Unit (None) 601 +# 0x55, 0x00, // Unit Exponent (0) 603 +# 0x05, 0x0d, // Usage Page (Digitizers) 605 +# 0x09, 0x30, // Usage (Tip Pressure) 607 +# 0x26, 0xff, 0x0f, // Logical Maximum (4095) 609 +# 0x75, 0x10, // Report Size (16) 612 +# 0x81, 0x02, // Input (Data,Var,Abs) 614 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 616 +# 0x09, 0x5b, // Usage (Vendor Usage 0x5b) 619 +# 0x16, 0x00, 0x80, // Logical Minimum (-32768) 621 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 624 +# 0x75, 0x10, // Report Size (16) 627 +# 0x81, 0x02, // Input (Data,Var,Abs) 629 +# 0x05, 0x0d, // Usage Page (Digitizers) 631 +# 0x09, 0x5b, // Usage (Transducer Serial Number) 633 +# 0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483648) 635 +# 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 640 +# 0x75, 0x20, // Report Size (32) 645 +# 0x81, 0x02, // Input (Data,Var,Abs) 647 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 649 +# 0x09, 0x00, // Usage (Undefined) 652 +# 0x75, 0x08, // Report Size (8) 654 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 656 +# 0x15, 0x00, // Logical Minimum (0) 659 +# 0x81, 0x02, // Input (Data,Var,Abs) 661 +# 0x05, 0x0d, // Usage Page (Digitizers) 663 +# 0x09, 0x3b, // Usage (Battery Strength) 665 +# 0x81, 0x02, // Input (Data,Var,Abs) 667 +# 0x65, 0x14, // Unit (Degrees,EngRotation) 669 +# 0x55, 0x00, // Unit Exponent (0) 671 +# 0x16, 0xa6, 0xff, // Logical Minimum (-90) 673 +# 0x26, 0x5a, 0x00, // Logical Maximum (90) 676 +# 0x36, 0xa6, 0xff, // Physical Minimum (-90) 679 +# 0x46, 0x5a, 0x00, // Physical Maximum (90) 682 +# 0x75, 0x08, // Report Size (8) 685 +# 0x09, 0x3d, // Usage (X Tilt) 687 +# 0x81, 0x02, // Input (Data,Var,Abs) 689 +# 0x09, 0x3e, // Usage (Y Tilt) 691 +# 0x81, 0x02, // Input (Data,Var,Abs) 693 +# 0xc0, // End Collection 695 +# 0xb4, // Pop 696 +# 0x85, 0x13, // Report ID (19) 697 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 699 +# 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 702 +# 0x96, 0x00, 0x01, // Report Count (256) 704 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 707 +# 0xc0, // End Collection 709 +# 0x06, 0x11, 0xff, // Usage Page (Vendor Usage Page 0xff11) 710 +# 0x09, 0x02, // Usage (Vendor Usage 0x02) 713 +# 0xa1, 0x01, // Collection (Application) 715 +# 0x85, 0x0b, // Report ID (11) 717 +# 0xa4, // Push 719 +# 0x09, 0x20, // Usage (Vendor Usage 0x20) 720 +# 0xa1, 0x00, // Collection (Physical) 722 +# 0x09, 0x42, // Usage (Vendor Usage 0x42) 724 +# 0x09, 0x44, // Usage (Vendor Usage 0x44) 726 +# 0x09, 0x45, // Usage (Vendor Usage 0x45) 728 +# 0x09, 0x3c, // Usage (Vendor Usage 0x3c) 730 +# 0x09, 0x5a, // Usage (Vendor Usage 0x5a) 732 +# 0x09, 0x32, // Usage (Vendor Usage 0x32) 734 +# 0x15, 0x00, // Logical Minimum (0) 736 +# 0x25, 0x01, // Logical Maximum (1) 738 +# 0x75, 0x01, // Report Size (1) 740 +# 0x95, 0x06, // Report Count (6) 742 +# 0x81, 0x02, // Input (Data,Var,Abs) 744 +# 0x95, 0x02, // Report Count (2) 746 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 748 +# 0x05, 0x01, // Usage Page (Generic Desktop) 750 +# 0x09, 0x30, // Usage (X) 752 +# 0x27, 0x70, 0x86, 0x00, 0x00, // Logical Maximum (34416) 754 +# 0x47, 0x70, 0x86, 0x00, 0x00, // Physical Maximum (34416) 759 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 764 +# 0x55, 0x0d, // Unit Exponent (-3) 766 +# 0x75, 0x10, // Report Size (16) 768 +# 0x95, 0x01, // Report Count (1) 770 +# 0x81, 0x02, // Input (Data,Var,Abs) 772 +# 0x09, 0x31, // Usage (Y) 774 +# 0x27, 0x9f, 0x4b, 0x00, 0x00, // Logical Maximum (19359) 776 +# 0x47, 0x9f, 0x4b, 0x00, 0x00, // Physical Maximum (19359) 781 +# 0x81, 0x02, // Input (Data,Var,Abs) 786 +# 0x45, 0x00, // Physical Maximum (0) 788 +# 0x65, 0x00, // Unit (None) 790 +# 0x55, 0x00, // Unit Exponent (0) 792 +# 0x05, 0x0d, // Usage Page (Digitizers) 794 +# 0x09, 0x30, // Usage (Tip Pressure) 796 +# 0x26, 0xff, 0x0f, // Logical Maximum (4095) 798 +# 0x75, 0x10, // Report Size (16) 801 +# 0x81, 0x02, // Input (Data,Var,Abs) 803 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 805 +# 0x09, 0x5b, // Usage (Vendor Usage 0x5b) 808 +# 0x16, 0x00, 0x80, // Logical Minimum (-32768) 810 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 813 +# 0x75, 0x10, // Report Size (16) 816 +# 0x81, 0x02, // Input (Data,Var,Abs) 818 +# 0x05, 0x0d, // Usage Page (Digitizers) 820 +# 0x09, 0x5b, // Usage (Transducer Serial Number) 822 +# 0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483648) 824 +# 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 829 +# 0x75, 0x20, // Report Size (32) 834 +# 0x81, 0x02, // Input (Data,Var,Abs) 836 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 838 +# 0x09, 0x00, // Usage (Undefined) 841 +# 0x75, 0x08, // Report Size (8) 843 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 845 +# 0x15, 0x00, // Logical Minimum (0) 848 +# 0x81, 0x02, // Input (Data,Var,Abs) 850 +# 0x05, 0x0d, // Usage Page (Digitizers) 852 +# 0x09, 0x3b, // Usage (Battery Strength) 854 +# 0x81, 0x02, // Input (Data,Var,Abs) 856 +# 0x65, 0x14, // Unit (Degrees,EngRotation) 858 +# 0x55, 0x00, // Unit Exponent (0) 860 +# 0x16, 0xa6, 0xff, // Logical Minimum (-90) 862 +# 0x26, 0x5a, 0x00, // Logical Maximum (90) 865 +# 0x36, 0xa6, 0xff, // Physical Minimum (-90) 868 +# 0x46, 0x5a, 0x00, // Physical Maximum (90) 871 +# 0x75, 0x08, // Report Size (8) 874 +# 0x09, 0x3d, // Usage (X Tilt) 876 +# 0x81, 0x02, // Input (Data,Var,Abs) 878 +# 0x09, 0x3e, // Usage (Y Tilt) 880 +# 0x81, 0x02, // Input (Data,Var,Abs) 882 +# 0xc0, // End Collection 884 +# 0xb4, // Pop 885 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 886 +# 0x75, 0x08, // Report Size (8) 889 +# 0x15, 0x00, // Logical Minimum (0) 891 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 893 +# 0x85, 0x05, // Report ID (5) 896 +# 0x09, 0x00, // Usage (Undefined) 898 +# 0x95, 0x3a, // Report Count (58) 900 +# 0x81, 0x02, // Input (Data,Var,Abs) 902 +# 0x85, 0x10, // Report ID (16) 904 +# 0x09, 0x00, // Usage (Undefined) 906 +# 0x95, 0x14, // Report Count (20) 908 +# 0x81, 0x02, // Input (Data,Var,Abs) 910 +# 0x85, 0x0f, // Report ID (15) 912 +# 0x09, 0x00, // Usage (Undefined) 914 +# 0x95, 0x28, // Report Count (40) 916 +# 0x81, 0x02, // Input (Data,Var,Abs) 918 +# 0x85, 0x0f, // Report ID (15) 920 +# 0x09, 0x00, // Usage (Undefined) 922 +# 0x95, 0x07, // Report Count (7) 924 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 926 +# 0x85, 0x11, // Report ID (17) 928 +# 0x09, 0x00, // Usage (Undefined) 930 +# 0x95, 0x09, // Report Count (9) 932 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 934 +# 0x85, 0x05, // Report ID (5) 936 +# 0x09, 0x00, // Usage (Undefined) 938 +# 0x95, 0x08, // Report Count (8) 940 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 942 +# 0x85, 0x10, // Report ID (16) 944 +# 0x09, 0x00, // Usage (Undefined) 946 +# 0x96, 0x3f, 0x00, // Report Count (63) 948 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 951 +# 0x85, 0x0b, // Report ID (11) 953 +# 0x09, 0x00, // Usage (Undefined) 955 +# 0x96, 0x3f, 0x00, // Report Count (63) 957 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 960 +# 0xc0, // End Collection 962 +# 0x05, 0x01, // Usage Page (Generic Desktop) 963 +# 0x09, 0x02, // Usage (Mouse) 965 +# 0xa1, 0x01, // Collection (Application) 967 +# 0x85, 0x01, // Report ID (1) 969 +# 0x09, 0x01, // Usage (Pointer) 971 +# 0xa1, 0x00, // Collection (Physical) 973 +# 0x05, 0x09, // Usage Page (Button) 975 +# 0x19, 0x01, // Usage Minimum (1) 977 +# 0x29, 0x02, // Usage Maximum (2) 979 +# 0x15, 0x00, // Logical Minimum (0) 981 +# 0x25, 0x01, // Logical Maximum (1) 983 +# 0x95, 0x02, // Report Count (2) 985 +# 0x75, 0x01, // Report Size (1) 987 +# 0x81, 0x02, // Input (Data,Var,Abs) 989 +# 0x95, 0x01, // Report Count (1) 991 +# 0x75, 0x06, // Report Size (6) 993 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 995 +# 0x05, 0x01, // Usage Page (Generic Desktop) 997 +# 0x09, 0x30, // Usage (X) 999 +# 0x09, 0x31, // Usage (Y) 1001 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 1003 +# 0x75, 0x10, // Report Size (16) 1006 +# 0x95, 0x02, // Report Count (2) 1008 +# 0x81, 0x02, // Input (Data,Var,Abs) 1010 +# 0xc0, // End Collection 1012 +# 0xc0, // End Collection 1013 diff --git a/plugins/wacom-raw/fu-plugin-wacom-raw.c b/plugins/wacom-raw/fu-plugin-wacom-raw.c new file mode 100644 index 000000000..22e4882f4 --- /dev/null +++ b/plugins/wacom-raw/fu-plugin-wacom-raw.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-wacom-aes-device.h" +#include "fu-wacom-emr-device.h" +#include "fu-wacom-common.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.wacom.raw"); + fu_plugin_add_udev_subsystem (plugin, "hidraw"); +} + +gboolean +fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_detach (device, error); +} + +gboolean +fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_attach (device, error); +} + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "hidraw") != 0) + return TRUE; + + /* wacom */ + if (fu_udev_device_get_vendor (device) != FU_WACOM_DEVICE_VID) + return TRUE; + + /* no actual device to open */ + if (g_udev_device_get_device_file (fu_udev_device_get_dev (device)) == NULL) + return TRUE; + + /* EMR */ + if (fu_device_has_guid (FU_DEVICE (device), "WacomEMR")) { + g_autoptr(FuWacomEmrDevice) dev = fu_wacom_emr_device_new (device); + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + } + + /* AES */ + if (fu_device_has_guid (FU_DEVICE (device), "WacomAES")) { + g_autoptr(FuWacomAesDevice) dev = fu_wacom_aes_device_new (device); + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + } + + /* not supported */ + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Only EMR or AES devices are supported"); + return FALSE; +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, error); +} diff --git a/plugins/wacom-raw/fu-wacom-aes-device.c b/plugins/wacom-raw/fu-wacom-aes-device.c new file mode 100644 index 000000000..5cfb55e5c --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-aes-device.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-chunk.h" +#include "fu-wacom-common.h" +#include "fu-wacom-aes-device.h" + +struct _FuWacomAesDevice { + FuWacomDevice parent_instance; + guint32 hwid; +}; + +G_DEFINE_TYPE (FuWacomAesDevice, fu_wacom_aes_device, FU_TYPE_WACOM_DEVICE) + +static gboolean +fu_wacom_aes_device_obtain_hwid (FuWacomAesDevice *self, GError **error) +{ + guint8 cmd[FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ] = { 0x0 }; + guint8 buf[FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ] = { 0x0 }; + + cmd[0] = FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID; + cmd[1] = 0x01; /* ?? */ + cmd[2] = 0x01; /* ?? */ + cmd[3] = 0x0f; /* ?? */ + + if (!fu_wacom_device_set_feature (FU_WACOM_DEVICE (self), + cmd, sizeof(cmd), error)) { + g_prefix_error (error, "failed to send: "); + return FALSE; + } + buf[0] = FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID; + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to receive: "); + return FALSE; + } + if (buf[1] == 0xff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "firmware does not support this feature"); + return FALSE; + } + + /* check magic number */ + if (memcmp (buf, "\x34\x12\x78\x56\x65\x87\x21\x43", 8) != 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "incorrect magic number"); + return FALSE; + } + + /* format the value */ + self->hwid = ((guint32) buf[9]) << 24 | + ((guint32) buf[8]) << 16 | + ((guint32) buf[11]) << 8 | + ((guint32) buf[10]); + return TRUE; + +} + +static gboolean +fu_wacom_aes_query_operation_mode (FuWacomAesDevice *self, GError **error) +{ + guint8 buf[FU_WACOM_RAW_FW_REPORT_SZ] = { + FU_WACOM_RAW_FW_REPORT_ID, + FU_WACOM_RAW_FW_CMD_QUERY_MODE, + }; + + /* 0x00=runtime, 0x02=bootloader */ + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), buf, sizeof(buf), error)) + return FALSE; + if (buf[1] == 0x00) { + fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; + } + if (buf[1] == 0x02) { + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; + } + + /* unsupported */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to query operation mode, got 0x%x", + buf[1]); + return FALSE; +} + +static gboolean +fu_wacom_aes_device_setup (FuDevice *device, GError **error) +{ + FuWacomAesDevice *self = FU_WACOM_AES_DEVICE (device); + + /* find out if in bootloader mode already */ + if (!fu_wacom_aes_query_operation_mode (self, error)) + return FALSE; + + /* get firmware version */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_set_version (device, "0.0"); + } else { + guint32 fw_ver; + guint8 data[FU_WACOM_RAW_STATUS_REPORT_SZ] = { + FU_WACOM_RAW_STATUS_REPORT_ID, + 0x0 + }; + g_autofree gchar *version = NULL; + g_autoptr(GError) error_local = NULL; + + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), + data, sizeof(data), error)) + return FALSE; + fw_ver = fu_common_read_uint16 (data + 11, G_LITTLE_ENDIAN); + version = g_strdup_printf ("%04x.%02x", fw_ver, data[13]); + fu_device_set_version (device, version); + + /* get the optional 32 byte HWID and add it as a GUID */ + if (!fu_wacom_aes_device_obtain_hwid (self, &error_local)) { + g_debug ("failed to get HwID: %s", error_local->message); + } else { + g_autofree gchar *guid = NULL; + guid = g_strdup_printf ("WACOM\\HWID_%04X", self->hwid); + fu_device_add_guid (device, guid); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_aes_device_erase_all (FuWacomAesDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ALL_ERASE, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, + 2000 * 1000, /* 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 (2 * G_USEC_PER_SEC); + return TRUE; +} + +static gboolean +fu_wacom_aes_device_write_block (FuWacomAesDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + guint16 datasz, + GError **error) +{ + guint blocksz = fu_wacom_device_get_block_sz (FU_WACOM_DEVICE (self)); + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_WRITE_FLASH, + .echo = (guint8) idx + 1, + .addr = GUINT32_TO_LE(address), + .size8 = datasz / 8, + .data = { 0x00 }, + }; + FuWacomRawResponse rsp = { 0x00 }; + + /* check size */ + if (datasz != blocksz) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "block size 0x%x != 0x%x untested", + datasz, (guint) blocksz); + return FALSE; + } + memcpy (&req.data, data, datasz); + + /* write */ + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 1000, + FU_WACOM_DEVICE_CMD_FLAG_NONE, 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, GPtrArray *chunks, GError **error) +{ + FuWacomAesDevice *self = FU_WACOM_AES_DEVICE (device); + + /* erase */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_wacom_aes_device_erase_all (self, error)) + return FALSE; + + /* write */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (!fu_wacom_aes_device_write_block (self, + chk->idx, + chk->address, + chk->data, + chk->data_sz, + error)) + return FALSE; + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + return TRUE; +} + +static void +fu_wacom_aes_device_init (FuWacomAesDevice *self) +{ + fu_device_set_name (FU_DEVICE (self), "Embedded Wacom AES Device"); +} + +static void +fu_wacom_aes_device_class_init (FuWacomAesDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuWacomDeviceClass *klass_wac_device = FU_WACOM_DEVICE_CLASS (klass); + klass_device->setup = fu_wacom_aes_device_setup; + klass_wac_device->write_firmware = fu_wacom_aes_device_write_firmware; +} + +FuWacomAesDevice * +fu_wacom_aes_device_new (FuUdevDevice *device) +{ + FuWacomAesDevice *self = g_object_new (FU_TYPE_WACOM_AES_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/wacom-raw/fu-wacom-aes-device.h b/plugins/wacom-raw/fu-wacom-aes-device.h new file mode 100644 index 000000000..07c61c5b8 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-aes-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_AES_DEVICE_H +#define __FU_WACOM_AES_DEVICE_H + +#include "fu-wacom-device.h" + +G_BEGIN_DECLS + +#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) + +FuWacomAesDevice *fu_wacom_aes_device_new (FuUdevDevice *device); + +G_END_DECLS + +#endif /* __FU_WACOM_AES_DEVICE_H */ diff --git a/plugins/wacom-raw/fu-wacom-common.c b/plugins/wacom-raw/fu-wacom-common.c new file mode 100644 index 000000000..07fd250a3 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-common.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wacom-common.h" + +gboolean +fu_wacom_common_check_reply (const FuWacomRawRequest *req, + const FuWacomRawResponse *rsp, + GError **error) +{ + if (rsp->report_id != FU_WACOM_RAW_BL_REPORT_ID_GET) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "report ID failed, expected 0x%02x, got 0x%02x", + (guint) FU_WACOM_RAW_BL_REPORT_ID_GET, + req->report_id); + return FALSE; + } + if (req->cmd != rsp->cmd) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cmd failed, expected 0x%02x, got 0x%02x", + req->cmd, rsp->cmd); + return FALSE; + } + if (req->echo != rsp->echo) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "echo failed, expected 0x%02x, got 0x%02x", + req->echo, rsp->echo); + return FALSE; + } + return TRUE; +} + +gboolean +fu_wacom_common_rc_set_error (const FuWacomRawResponse *rsp, GError **error) +{ + if (rsp->resp == FU_WACOM_RAW_RC_OK) + return TRUE; + if (rsp->resp == FU_WACOM_RAW_RC_BUSY) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_BUSY, + "device is busy"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_MCUTYPE) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "MCU type does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_PID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "PID does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_CHECKSUM1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum1 does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_CHECKSUM2) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum2 does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_TIMEOUT) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "command timed out"); + return FALSE; + } + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unknown error 0x%02x", rsp->resp); + 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 --git a/plugins/wacom-raw/fu-wacom-common.h b/plugins/wacom-raw/fu-wacom-common.h new file mode 100644 index 000000000..c142c0e48 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-common.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_COMMON_H +#define __FU_WACOM_COMMON_H + +#include + +G_BEGIN_DECLS + +#define FU_WACOM_DEVICE_VID 0x056A +#define FU_WACOM_RAW_CMD_RETRIES 1000 + +#define FU_WACOM_RAW_STATUS_REPORT_ID 0x04 +#define FU_WACOM_RAW_STATUS_REPORT_SZ 16 + +#define FU_WACOM_RAW_FW_REPORT_ID 0x02 +#define FU_WACOM_RAW_FW_CMD_QUERY_MODE 0x00 +#define FU_WACOM_RAW_FW_CMD_DETACH 0x02 +#define FU_WACOM_RAW_FW_REPORT_SZ 2 + +#define FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID 0x09 +#define FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ 64 + +#define FU_WACOM_RAW_BL_REPORT_ID_SET 0x07 +#define FU_WACOM_RAW_BL_REPORT_ID_GET 0x08 + +#define FU_WACOM_RAW_BL_CMD_ERASE_FLASH 0x00 +#define FU_WACOM_RAW_BL_CMD_WRITE_FLASH 0x01 +#define FU_WACOM_RAW_BL_CMD_VERIFY_FLASH 0x02 +#define FU_WACOM_RAW_BL_CMD_ATTACH 0x03 +#define FU_WACOM_RAW_BL_CMD_GET_BLVER 0x04 +#define FU_WACOM_RAW_BL_CMD_GET_MPUTYPE 0x05 +#define FU_WACOM_RAW_BL_CMD_CHECK_MODE 0x07 +#define FU_WACOM_RAW_BL_CMD_ERASE_DATAMEM 0x0e +#define FU_WACOM_RAW_BL_CMD_ALL_ERASE 0x90 + +#define FU_WACOM_RAW_RC_OK 0x00 +#define FU_WACOM_RAW_RC_BUSY 0x80 +#define FU_WACOM_RAW_RC_MCUTYPE 0x0c +#define FU_WACOM_RAW_RC_PID 0x0d +#define FU_WACOM_RAW_RC_CHECKSUM1 0x81 +#define FU_WACOM_RAW_RC_CHECKSUM2 0x82 +#define FU_WACOM_RAW_RC_TIMEOUT 0x87 +#define FU_WACOM_RAW_RC_IN_PROGRESS 0xff + +#define FU_WACOM_RAW_ECHO_DEFAULT g_random_int_range(0xa0,0xfe) + +typedef struct __attribute__((packed)) { + guint8 report_id; + guint8 cmd; + guint8 echo; + guint32 addr; + guint8 size8; + guint8 data[128]; + guint8 data_unused[121]; +} FuWacomRawRequest; + +typedef struct __attribute__((packed)) { + guint8 report_id; + guint8 cmd; + guint8 echo; + guint8 resp; + guint8 data_unused[132]; +} FuWacomRawResponse; + +gboolean fu_wacom_common_rc_set_error (const FuWacomRawResponse *rsp, + GError **error); +gboolean fu_wacom_common_check_reply (const FuWacomRawRequest *req, + const FuWacomRawResponse *rsp, + GError **error); +gboolean fu_wacom_common_block_is_empty (const guint8 *data, + guint16 datasz); + +G_END_DECLS + +#endif /* __FU_WACOM_COMMON_H */ diff --git a/plugins/wacom-raw/fu-wacom-device.c b/plugins/wacom-raw/fu-wacom-device.c new file mode 100644 index 000000000..ddeb85879 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-device.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#include "fu-chunk.h" +#include "fu-wacom-common.h" +#include "fu-wacom-device.h" +#include "dfu-firmware.h" + +typedef struct +{ + gint fd; + guint flash_block_size; + guint32 flash_base_addr; + guint32 flash_size; +} FuWacomDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuWacomDevice, fu_wacom_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_wacom_device_get_instance_private (o)) + +static void +fu_wacom_device_to_string (FuDevice *device, GString *str) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + g_string_append (str, " FuWacomDevice:\n"); + g_string_append_printf (str, " fd:\t\t\t%i\n", priv->fd); + g_string_append_printf (str, " flash-block-size:\t0x%04x\n", priv->flash_block_size); + g_string_append_printf (str, " flash-base-addr:\t0x%04x\n", priv->flash_base_addr); + g_string_append_printf (str, " flash-size:\t\t0x%04x\n", priv->flash_size); +} + +guint +fu_wacom_device_get_block_sz (FuWacomDevice *self) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + return priv->flash_block_size; +} + +guint +fu_wacom_device_get_base_addr (FuWacomDevice *self) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + return priv->flash_base_addr; +} + +gboolean +fu_wacom_device_check_mpu (FuWacomDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_GET_MPUTYPE, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 0, + FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, error)) { + g_prefix_error (error, "failed to get MPU type: "); + return FALSE; + } + + /* W9013 */ + if (rsp.resp == 0x2e) { + fu_device_add_guid (FU_DEVICE (self), "WacomEMR_W9013"); + return TRUE; + } + + /* W9021 */ + if (rsp.resp == 0x45) { + fu_device_add_guid (FU_DEVICE (self), "WacomEMR_W9021"); + return TRUE; + } + + /* unsupported */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "MPU is not W9013 or W9021: 0x%x", + rsp.resp); + return FALSE; +} + +static gboolean +fu_wacom_device_open (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + + /* open device */ + priv->fd = g_open (g_udev_device_get_device_file (udev_device), O_RDWR); + if (priv->fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open %s", + g_udev_device_get_device_file (udev_device)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_device_close (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + if (!g_close (priv->fd, error)) + return FALSE; + priv->fd = 0; + return TRUE; +} + +static gboolean +fu_wacom_device_probe (FuUdevDevice *device, GError **error) +{ + /* set the physical ID */ + if (!fu_udev_device_set_physical_id (device, "hid", error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_wacom_device_detach (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + guint8 buf[FU_WACOM_RAW_FW_REPORT_SZ] = { + FU_WACOM_RAW_FW_REPORT_ID, + FU_WACOM_RAW_FW_CMD_DETACH, + }; + if (!fu_wacom_device_set_feature (self, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to switch to bootloader mode: "); + return FALSE; + } + g_usleep (300 * 1000); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_wacom_device_attach (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomRawRequest req = { + .report_id = FU_WACOM_RAW_BL_REPORT_ID_SET, + .cmd = FU_WACOM_RAW_BL_CMD_ATTACH, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + if (!fu_wacom_device_set_feature (self, (const guint8 *) &req, sizeof(req), error)) { + g_prefix_error (error, "failed to switch to runtime mode: "); + return FALSE; + } + /* only required on AES, but harmless for EMR */ + g_usleep (300 * 1000); + fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_wacom_device_check_mode (FuWacomDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_CHECK_MODE, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (self, &req, &rsp, 0, + FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, error)) { + g_prefix_error (error, "failed to check mode: "); + return FALSE; + } + if (rsp.resp != 0x06) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "check mode failed, mode=0x%02x", + rsp.resp); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wacom_device_set_version_bootloader (FuWacomDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_GET_BLVER, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + g_autofree gchar *version = NULL; + if (!fu_wacom_device_cmd (self, &req, &rsp, 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.resp); + fu_device_set_version_bootloader (FU_DEVICE (self), version); + return TRUE; +} + +static gboolean +fu_wacom_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS (device); + DfuElement *element; + DfuImage *image; + GBytes *fw_new; + g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(GPtrArray) chunks = NULL; + + /* parse hex file */ + if (!dfu_firmware_parse_data (firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) + return FALSE; + if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_INTEL_HEX) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "expected firmware format is 'ihex', got '%s'", + dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); + return FALSE; + } + + /* use the correct image from the firmware */ + image = dfu_firmware_get_image_default (firmware); + if (image == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no firmware image"); + return FALSE; + } + element = dfu_image_get_element_default (image); + if (element == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no element in image"); + return FALSE; + } + g_debug ("using element at addr 0x%0x", + (guint) dfu_element_get_address (element)); + + /* check start address and size */ + if (dfu_element_get_address (element) != priv->flash_base_addr) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "base addr invalid: 0x%05x", + (guint) dfu_element_get_address (element)); + return FALSE; + } + fw_new = dfu_element_get_contents (element); + if (g_bytes_get_size (fw_new) > priv->flash_size) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "size is invalid: 0x%05x", + (guint) g_bytes_get_size (fw_new)); + 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_new, priv->flash_base_addr, + 0x00, /* page_sz */ + priv->flash_block_size); + return klass->write_firmware (device, chunks, error); +} + +gboolean +fu_wacom_device_set_feature (FuWacomDevice *self, + const guint8 *data, + guint datasz, + GError **error) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + + /* Set Feature */ + fu_common_dump_raw (G_LOG_DOMAIN, "SetFeature", data, datasz); + if (ioctl (priv->fd, HIDIOCSFEATURE(datasz), data) < 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to SetFeature"); + return FALSE; + } + return TRUE; +} + +gboolean +fu_wacom_device_get_feature (FuWacomDevice *self, + guint8 *data, + guint datasz, + GError **error) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + if (ioctl (priv->fd, HIDIOCGFEATURE(datasz), data) < 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to GetFeature"); + return FALSE; + } + fu_common_dump_raw (G_LOG_DOMAIN, "GetFeature", data, datasz); + return TRUE; +} + +gboolean +fu_wacom_device_cmd (FuWacomDevice *self, + FuWacomRawRequest *req, FuWacomRawResponse *rsp, + gulong delay_us, FuWacomDeviceCmdFlags flags, + GError **error) +{ + req->report_id = FU_WACOM_RAW_BL_REPORT_ID_SET; + if (!fu_wacom_device_set_feature (self, (const guint8 *)req, sizeof(*req), error)) { + g_prefix_error (error, "failed to send: "); + return FALSE; + } + if (delay_us > 0) + g_usleep (delay_us); + rsp->report_id = FU_WACOM_RAW_BL_REPORT_ID_GET; + if (!fu_wacom_device_get_feature (self, (guint8 *)rsp, sizeof(*rsp), error)) { + g_prefix_error (error, "failed to receive: "); + return FALSE; + } + if (flags & FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK) + return TRUE; + if (!fu_wacom_common_check_reply (req, rsp, error)) + return FALSE; + + /* wait for the command to complete */ + if (flags & FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING && + rsp->resp != FU_WACOM_RAW_RC_OK) { + for (guint i = 0; i < FU_WACOM_RAW_CMD_RETRIES; i++) { + if (delay_us > 0) + g_usleep (delay_us); + if (!fu_wacom_device_get_feature (self, (guint8 *)rsp, sizeof(*rsp), error)) + return FALSE; + if (!fu_wacom_common_check_reply (req, rsp, error)) + return FALSE; + if (rsp->resp != FU_WACOM_RAW_RC_IN_PROGRESS && + rsp->resp != FU_WACOM_RAW_RC_BUSY) + break; + } + } + return fu_wacom_common_rc_set_error (rsp, 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); + if (g_strcmp0 (key, "WacomI2cFlashBlockSize") == 0) { + priv->flash_block_size = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "WacomI2cFlashBaseAddr") == 0) { + priv->flash_base_addr = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "WacomI2cFlashSize") == 0) { + priv->flash_size = fu_common_strtoull (value); + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_wacom_device_init (FuWacomDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static void +fu_wacom_device_class_init (FuWacomDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); + klass_device->to_string = fu_wacom_device_to_string; + klass_device->open = fu_wacom_device_open; + klass_device->close = fu_wacom_device_close; + klass_device->write_firmware = fu_wacom_device_write_firmware; + klass_device->attach = fu_wacom_device_attach; + klass_device->detach = fu_wacom_device_detach; + klass_device->set_quirk_kv = fu_wacom_device_set_quirk_kv; + klass_device_udev->probe = fu_wacom_device_probe; +} diff --git a/plugins/wacom-raw/fu-wacom-device.h b/plugins/wacom-raw/fu-wacom-device.h new file mode 100644 index 000000000..383ceb3b7 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-device.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_DEVICE_H +#define __FU_WACOM_DEVICE_H + +#include "fu-wacom-common.h" +#include "fu-udev-device.h" +#include "dfu-element.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WACOM_DEVICE (fu_wacom_device_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuWacomDevice, fu_wacom_device, FU, WACOM_DEVICE, FuUdevDevice) + +struct _FuWacomDeviceClass +{ + FuUdevDeviceClass parent_class; + gboolean (*write_firmware) (FuDevice *self, + GPtrArray *chunks, + 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; + +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, + FuWacomRawRequest *req, + FuWacomRawResponse *rsp, + gulong delay_us, + FuWacomDeviceCmdFlags flags, + GError **error); +gboolean fu_wacom_device_erase_all (FuWacomDevice *self, + GError **error); +gboolean fu_wacom_device_check_mpu (FuWacomDevice *self, + GError **error); +guint fu_wacom_device_get_block_sz (FuWacomDevice *self); +guint fu_wacom_device_get_base_addr (FuWacomDevice *self); + +G_END_DECLS + +#endif /* __FU_WACOM_DEVICE_H */ diff --git a/plugins/wacom-raw/fu-wacom-emr-device.c b/plugins/wacom-raw/fu-wacom-emr-device.c new file mode 100644 index 000000000..b554c4640 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-emr-device.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-chunk.h" +#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 (device, "0.0"); + } else { + guint16 fw_ver; + guint8 data[19] = { 0x03, 0x0 }; /* 0x03 is an unknown ReportID */ + g_autofree gchar *version = NULL; + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), + data, sizeof(data), error)) + return FALSE; + fw_ver = fu_common_read_uint16 (data + 11, G_LITTLE_ENDIAN); + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + version = fu_common_version_from_uint16 (fw_ver, FU_VERSION_FORMAT_PAIR); + fu_device_set_version (device, version); + } + + /* success */ + return TRUE; +} + +static guint8 +fu_wacom_emr_device_calc_checksum (guint8 init1, const guint8 *buf, guint8 bufsz) +{ + guint8 sum = 0; + sum += init1; + for (guint i = 0; i < bufsz; i++) + sum += buf[i]; + return ~sum + 1; +} + +static gboolean +fu_wacom_emr_device_w9013_erase_data (FuWacomEmrDevice *self, GError **error) +{ + FuWacomRawResponse rsp = { 0x00 }; + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ERASE_DATAMEM, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + guint8 *buf = (guint8 *) &req.addr; + buf[0] = 0x00; /* erased block */ + buf[1] = fu_wacom_emr_device_calc_checksum (0x05 + 0x00 + 0x07 + 0x00, + (const guint8 *) &req, 4); + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 50, + 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) +{ + FuWacomRawResponse rsp = { 0x00 }; + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ERASE_FLASH, + .echo = idx, + 0x00 + }; + guint8 *buf = (guint8 *) &req.addr; + buf[0] = block_nr; + buf[1] = fu_wacom_emr_device_calc_checksum (0x05 + 0x00 + 0x07 + 0x00, + (const guint8 *) &req, 4); + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 50, + 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_device_w9021_erase_all (FuWacomEmrDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ALL_ERASE, + .echo = 0x01, + .addr = 0x00, + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, + 2000 * 1000, /* 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; + } + if (!fu_wacom_common_rc_set_error (&rsp, error)) { + g_prefix_error (error, "failed to erase"); + return FALSE; + } + g_usleep (50); + return TRUE; +} + +static gboolean +fu_wacom_emr_device_write_block (FuWacomEmrDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + guint16 datasz, + GError **error) +{ + guint blocksz = fu_wacom_device_get_block_sz (FU_WACOM_DEVICE (self)); + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_WRITE_FLASH, + .echo = (guint8) idx + 1, + .addr = GUINT32_TO_LE(address), + .size8 = datasz / 8, + .data = { 0x00 }, + }; + FuWacomRawResponse rsp = { 0x00 }; + + /* check size */ + if (datasz > sizeof(req.data)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "data size 0x%x too large for packet", + datasz); + return FALSE; + } + if (datasz != blocksz) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "block size 0x%x != 0x%x untested", + datasz, (guint) blocksz); + return FALSE; + } + + /* data */ + memcpy (&req.data, data, datasz); + + /* cmd and data checksums */ + req.data[blocksz + 0] = fu_wacom_emr_device_calc_checksum (0x05 + 0x00 + 0x4c + 0x00, + (const guint8 *) &req, 8); + req.data[blocksz + 1] = fu_wacom_emr_device_calc_checksum (0x00, data, datasz); + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 50, + 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, GPtrArray *chunks, GError **error) +{ + FuWacomEmrDevice *self = FU_WACOM_EMR_DEVICE (device); + guint8 idx = 0; + + /* erase W9013 */ + if (fu_device_has_guid (device, "WacomEMR_W9013")) { + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + 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_guid (device, "WacomEMR_W9021")) { + if (!fu_wacom_device_w9021_erase_all (self, error)) + return FALSE; + } + + /* write */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (fu_wacom_common_block_is_empty (chk->data, chk->data_sz)) + continue; + if (!fu_wacom_emr_device_write_block (self, + chk->idx, + chk->address, + chk->data, + chk->data_sz, + error)) + return FALSE; + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + + fu_device_set_progress (device, 100); + return TRUE; +} + +static void +fu_wacom_emr_device_init (FuWacomEmrDevice *self) +{ + fu_device_set_name (FU_DEVICE (self), "Embedded Wacom EMR Device"); +} + +static void +fu_wacom_emr_device_class_init (FuWacomEmrDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuWacomDeviceClass *klass_wac_device = FU_WACOM_DEVICE_CLASS (klass); + klass_device->setup = fu_wacom_emr_device_setup; + klass_wac_device->write_firmware = fu_wacom_emr_device_write_firmware; +} + +FuWacomEmrDevice * +fu_wacom_emr_device_new (FuUdevDevice *device) +{ + FuWacomEmrDevice *self = g_object_new (FU_TYPE_WACOM_EMR_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/wacom-raw/fu-wacom-emr-device.h b/plugins/wacom-raw/fu-wacom-emr-device.h new file mode 100644 index 000000000..6a7cc35be --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-emr-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_EMR_DEVICE_H +#define __FU_WACOM_EMR_DEVICE_H + +#include "fu-wacom-device.h" + +G_BEGIN_DECLS + +#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, FuUdevDevice) + +FuWacomEmrDevice *fu_wacom_emr_device_new (FuUdevDevice *device); + +G_END_DECLS + +#endif /* __FU_WACOM_EMR_DEVICE_H */ diff --git a/plugins/wacom-raw/meson.build b/plugins/wacom-raw/meson.build new file mode 100644 index 000000000..ec0b63f70 --- /dev/null +++ b/plugins/wacom-raw/meson.build @@ -0,0 +1,31 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginWacomRaw"'] + +install_data(['wacom-raw.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_wacom_raw', + fu_hash, + sources : [ + 'fu-plugin-wacom-raw.c', + 'fu-wacom-common.c', + 'fu-wacom-device.c', + 'fu-wacom-aes-device.c', + 'fu-wacom-emr-device.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../dfu'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + c_args : cargs, + dependencies : [ + plugin_deps, + ], + link_with : [ + dfu, + ], +) diff --git a/plugins/wacom-raw/wacom-raw.quirk b/plugins/wacom-raw/wacom-raw.quirk new file mode 100644 index 000000000..51ceee1a8 --- /dev/null +++ b/plugins/wacom-raw/wacom-raw.quirk @@ -0,0 +1,35 @@ +# Devices that do "replug" and thus don't change VID:PID to the bootloader +# need to have an extra GUID of WacomAES or WacomEMR added so that the flash +# constants are set correctly. + +# Dell XPS-15 9575 +[DeviceInstanceId=HIDRAW\VEN_056A&DEV_4875] +Plugin = wacom_raw +Guid = WacomAES + +# AES bootloader mode +[DeviceInstanceId=HIDRAW\VEN_056A&DEV_0094] +Plugin = wacom_raw +Guid = WacomAES +Flags = is-bootloader + +# EMR bootloader mode +[DeviceInstanceId=HIDRAW\VEN_056A&DEV_012B] +Plugin = wacom_raw +Guid = WacomEMR +Flags = is-bootloader + +[Guid=WacomEMR_W9013] +WacomI2cFlashBlockSize=64 +WacomI2cFlashBaseAddr=0x2000 +WacomI2cFlashSize=0x1e000 + +[Guid=WacomEMR_W9021] +WacomI2cFlashBlockSize=256 +WacomI2cFlashBaseAddr=0x3000 +WacomI2cFlashSize=0x3c000 + +[Guid=WacomAES] +WacomI2cFlashBlockSize=128 +WacomI2cFlashBaseAddr=0x8000 +WacomI2cFlashSize=0x24000 diff --git a/plugins/wacomhid/README.md b/plugins/wacom-usb/README.md similarity index 54% rename from plugins/wacomhid/README.md rename to plugins/wacom-usb/README.md index 69758aff2..e4bc759b8 100644 --- a/plugins/wacomhid/README.md +++ b/plugins/wacom-usb/README.md @@ -1,4 +1,4 @@ -Wacom HID Support +Wacom USB Support ================= Introduction @@ -15,3 +15,27 @@ Wacom devices are actually composite devices, with the main ARM CPU being programmed using a more complicated erase, write, verify algorithm based on a historical update protocol. The "sub-module" devices use a newer protocol, again based on HID, but are handled differently depending on thier type. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +the following formats: + + * Touch module: Intel HEX file format + * Bluetooth module: Unknown airoflash file format + * EMR module: Plain SREC file format + * Main module: SREC file format, with a custom `WACOM` vendor header + +This plugin supports the following protocol ID: + + * com.wacom.usb + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_056A&PID_0378&REV_0001` + * `USB\VID_056A&PID_0378` + * `USB\VID_056A` diff --git a/plugins/wacom-usb/data/lsusb.txt b/plugins/wacom-usb/data/lsusb.txt new file mode 100644 index 000000000..216f0b72c --- /dev/null +++ b/plugins/wacom-usb/data/lsusb.txt @@ -0,0 +1,58 @@ +Bus 001 Device 023: ID 056a:0378 Wacom Co., Ltd CTL-6100WL [Intuos BT (M)] +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x056a Wacom Co., Ltd + idProduct 0x0378 CTL-6100WL [Intuos BT (M)] + bcdDevice 1.66 + iManufacturer 1 Wacom Co.,Ltd. + iProduct 2 Intuos BT M + iSerial 3 8BH00U2012294 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0022 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + 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 759 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/plugins/wacomhid/fu-plugin-wacomhid.c b/plugins/wacom-usb/fu-plugin-wacom-usb.c similarity index 88% rename from plugins/wacomhid/fu-plugin-wacomhid.c rename to plugins/wacom-usb/fu-plugin-wacom-usb.c index ed20d5dc1..af82ae425 100644 --- a/plugins/wacomhid/fu-plugin-wacomhid.c +++ b/plugins/wacom-usb/fu-plugin-wacom-usb.c @@ -13,7 +13,9 @@ void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.wacom.usb"); } gboolean diff --git a/plugins/wacomhid/fu-self-test.c b/plugins/wacom-usb/fu-self-test.c similarity index 100% rename from plugins/wacomhid/fu-self-test.c rename to plugins/wacom-usb/fu-self-test.c diff --git a/plugins/wacomhid/fu-wac-common.c b/plugins/wacom-usb/fu-wac-common.c similarity index 100% rename from plugins/wacomhid/fu-wac-common.c rename to plugins/wacom-usb/fu-wac-common.c diff --git a/plugins/wacomhid/fu-wac-common.h b/plugins/wacom-usb/fu-wac-common.h similarity index 100% rename from plugins/wacomhid/fu-wac-common.h rename to plugins/wacom-usb/fu-wac-common.h diff --git a/plugins/wacomhid/fu-wac-device.c b/plugins/wacom-usb/fu-wac-device.c similarity index 100% rename from plugins/wacomhid/fu-wac-device.c rename to plugins/wacom-usb/fu-wac-device.c diff --git a/plugins/wacomhid/fu-wac-device.h b/plugins/wacom-usb/fu-wac-device.h similarity index 100% rename from plugins/wacomhid/fu-wac-device.h rename to plugins/wacom-usb/fu-wac-device.h diff --git a/plugins/wacomhid/fu-wac-firmware.c b/plugins/wacom-usb/fu-wac-firmware.c similarity index 100% rename from plugins/wacomhid/fu-wac-firmware.c rename to plugins/wacom-usb/fu-wac-firmware.c diff --git a/plugins/wacomhid/fu-wac-firmware.h b/plugins/wacom-usb/fu-wac-firmware.h similarity index 100% rename from plugins/wacomhid/fu-wac-firmware.h rename to plugins/wacom-usb/fu-wac-firmware.h diff --git a/plugins/wacomhid/fu-wac-module-bluetooth.c b/plugins/wacom-usb/fu-wac-module-bluetooth.c similarity index 100% rename from plugins/wacomhid/fu-wac-module-bluetooth.c rename to plugins/wacom-usb/fu-wac-module-bluetooth.c diff --git a/plugins/wacomhid/fu-wac-module-bluetooth.h b/plugins/wacom-usb/fu-wac-module-bluetooth.h similarity index 100% rename from plugins/wacomhid/fu-wac-module-bluetooth.h rename to plugins/wacom-usb/fu-wac-module-bluetooth.h diff --git a/plugins/wacomhid/fu-wac-module-touch.c b/plugins/wacom-usb/fu-wac-module-touch.c similarity index 100% rename from plugins/wacomhid/fu-wac-module-touch.c rename to plugins/wacom-usb/fu-wac-module-touch.c diff --git a/plugins/wacomhid/fu-wac-module-touch.h b/plugins/wacom-usb/fu-wac-module-touch.h similarity index 100% rename from plugins/wacomhid/fu-wac-module-touch.h rename to plugins/wacom-usb/fu-wac-module-touch.h diff --git a/plugins/wacomhid/fu-wac-module.c b/plugins/wacom-usb/fu-wac-module.c similarity index 100% rename from plugins/wacomhid/fu-wac-module.c rename to plugins/wacom-usb/fu-wac-module.c diff --git a/plugins/wacomhid/fu-wac-module.h b/plugins/wacom-usb/fu-wac-module.h similarity index 100% rename from plugins/wacomhid/fu-wac-module.h rename to plugins/wacom-usb/fu-wac-module.h diff --git a/plugins/wacomhid/meson.build b/plugins/wacom-usb/meson.build similarity index 82% rename from plugins/wacomhid/meson.build rename to plugins/wacom-usb/meson.build index 5cdf65cce..e2a3f37c7 100644 --- a/plugins/wacomhid/meson.build +++ b/plugins/wacom-usb/meson.build @@ -1,10 +1,11 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginWac"'] +cargs = ['-DG_LOG_DOMAIN="FuPluginWacomUsb"'] -install_data(['wacomhid.quirk'], +install_data(['wacom-usb.quirk'], install_dir: join_paths(datadir, 'fwupd', 'quirks.d') ) -shared_module('fu_plugin_wacomhid', +shared_module('fu_plugin_wacom_usb', + fu_hash, sources : [ 'fu-wac-common.c', 'fu-wac-device.c', @@ -12,7 +13,7 @@ shared_module('fu_plugin_wacomhid', 'fu-wac-module.c', 'fu-wac-module-bluetooth.c', 'fu-wac-module-touch.c', - 'fu-plugin-wacomhid.c', + 'fu-plugin-wacom-usb.c', ], include_directories : [ include_directories('../..'), @@ -27,6 +28,7 @@ shared_module('fu_plugin_wacomhid', plugin_deps, ], link_with : [ + libfwupdprivate, dfu, ], ) @@ -35,7 +37,7 @@ if get_option('tests') testdatadir = join_paths(meson.current_source_dir(), 'tests') cargs += '-DTESTDATADIR="' + testdatadir + '"' e = executable( - 'wacomhid-self-test', + 'wacom-usb-self-test', sources : [ 'fu-self-test.c', 'fu-wac-common.c', @@ -49,7 +51,7 @@ if get_option('tests') include_directories('../../src'), ], dependencies : [ - appstream_glib, + libxmlb, gio, gusb, gudev, @@ -57,10 +59,9 @@ if get_option('tests') ], link_with : [ dfu, - fwupd, libfwupdprivate, ], c_args : cargs ) - test('wacomhid-self-test', e) + test('wacom-usb-self-test', e) endif diff --git a/plugins/wacom-usb/wacom-usb.quirk b/plugins/wacom-usb/wacom-usb.quirk new file mode 100644 index 000000000..ac229566b --- /dev/null +++ b/plugins/wacom-usb/wacom-usb.quirk @@ -0,0 +1,58 @@ + +# Intuos Pro medium (2nd-gen BT) [PTH-660] +[DeviceInstanceId=USB\VID_056A&PID_0360] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos Pro large (2nd-gen BT) [PTH-860] +[DeviceInstanceId=USB\VID_056A&PID_0361] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos BT S (3rd-gen BT) [CTL-4100WL] +[DeviceInstanceId=USB\VID_056A&PID_0377] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos BT M (3rd-gen BT) [CTL-6100WL] +[DeviceInstanceId=USB\VID_056A&PID_0379] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos Pro medium (2nd-gen USB) [PTH-660] +[DeviceInstanceId=USB\VID_056A&PID_0357] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos Pro large (2nd-gen USB) [PTH-860] +[DeviceInstanceId=USB\VID_056A&PID_0358] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos S 3rd-gen (USB) [CTL-4100] +[DeviceInstanceId=USB\VID_056A&PID_0374] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos M 3rd-gen (USB) [NA] +[DeviceInstanceId=USB\VID_056A&PID_0375] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos BT S 3rd-gen (USB) [CTL-4100WL] +[DeviceInstanceId=USB\VID_056A&PID_0376] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos BT M 3rd-gen (USB) [CTL-6100WL] +[DeviceInstanceId=USB\VID_056A&PID_0378] +Plugin = wacom_usb +Flags = use-runtime-version + +# Intuos Pro Small (2nd-gen USB) [PTH-460] +[DeviceInstanceId=USB\VID_056A&PID_0392] +Plugin = wacom_usb + +# Intuos Pro Small (2nd-gen Bluetooth) [PTH-460] +[DeviceInstanceId=USB\VID_056A&PID_0393] +Plugin = wacom_usb diff --git a/plugins/wacomhid/wacomhid.quirk b/plugins/wacomhid/wacomhid.quirk deleted file mode 100644 index 643524232..000000000 --- a/plugins/wacomhid/wacomhid.quirk +++ /dev/null @@ -1,99 +0,0 @@ -# MobileStudio Pro 13 (touch) [DTH-W1320] -[DeviceInstanceId=USB\VID_056A&PID_034A] -Plugin = wacomhid -Flags = use-runtime-version - -# MobileStudio Pro 16 (touch) [DTH-W1620] -[DeviceInstanceId=USB\VID_056A&PID_034B] -Plugin = wacomhid -Flags = use-runtime-version - -# MobileStudio Pro 13 (pen/pad) [DTH-W1320] -[DeviceInstanceId=USB\VID_056A&PID_034D] -Plugin = wacomhid -Flags = use-runtime-version - -# MobileStudio Pro 16 (pen/pad) [DTH-W1620] -[DeviceInstanceId=USB\VID_056A&PID_034E] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos Pro medium (2nd-gen BT) [PTH-660] -[DeviceInstanceId=USB\VID_056A&PID_0360] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos Pro large (2nd-gen BT) [PTH-860] -[DeviceInstanceId=USB\VID_056A&PID_0361] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos BT S (3rd-gen BT) [CTL-4100WL] -[DeviceInstanceId=USB\VID_056A&PID_0377] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos BT M (3rd-gen BT) [CTL-6100WL] -[DeviceInstanceId=USB\VID_056A&PID_0379] -Plugin = wacomhid -Flags = use-runtime-version - -# Cintiq Pro 13 (pen/pad) [DTH-1320] -[DeviceInstanceId=USB\VID_056A&PID_034F] -Plugin = wacomhid -Flags = use-runtime-version - -# Cintiq Pro 16 (pen/pad) [DTH-1620] -[DeviceInstanceId=USB\VID_056A&PID_0350] -Plugin = wacomhid -Flags = use-runtime-version - -# Cintiq Pro 13 (touch) [DTH-1320] -[DeviceInstanceId=USB\VID_056A&PID_0353] -Plugin = wacomhid -Flags = use-runtime-version - -# Cintiq Pro 16 (touch) [DTH-1620] -[DeviceInstanceId=USB\VID_056A&PID_0354] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos Pro medium (2nd-gen USB) [PTH-660] -[DeviceInstanceId=USB\VID_056A&PID_0357] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos Pro large (2nd-gen USB) [PTH-860] -[DeviceInstanceId=USB\VID_056A&PID_0358] -Plugin = wacomhid -Flags = use-runtime-version - -# Pen [DTH-1152] -[DeviceInstanceId=USB\VID_056A&PID_035A] -Plugin = wacomhid -Flags = use-runtime-version - -# Touch [DTH-1152] -[DeviceInstanceId=USB\VID_056A&PID_0368] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos S 3rd-gen (USB) [CTL-4100] -[DeviceInstanceId=USB\VID_056A&PID_0374] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos M 3rd-gen (USB) [NA] -[DeviceInstanceId=USB\VID_056A&PID_0375] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos BT S 3rd-gen (USB) [CTL-4100WL] -[DeviceInstanceId=USB\VID_056A&PID_0376] -Plugin = wacomhid -Flags = use-runtime-version - -# Intuos BT M 3rd-gen (USB) [CTL-6100WL] -[DeviceInstanceId=USB\VID_056A&PID_0378] -Plugin = wacomhid -Flags = use-runtime-version diff --git a/po/LINGUAS b/po/LINGUAS index c96395e31..0c8caf910 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1,3 +1,4 @@ +af ast ca cs diff --git a/po/POTFILES.in b/po/POTFILES.in index 5fcb39037..701065707 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,3 @@ -data/org.freedesktop.fwupd.metainfo.xml data/remotes.d/lvfs.metainfo.xml data/remotes.d/lvfs-testing.metainfo.xml policy/org.freedesktop.fwupd.policy.in diff --git a/po/POTFILES.skip b/po/POTFILES.skip new file mode 100644 index 000000000..aff3fbe05 --- /dev/null +++ b/po/POTFILES.skip @@ -0,0 +1 @@ +data/org.freedesktop.fwupd.metainfo.xml diff --git a/po/af.po b/po/af.po new file mode 100644 index 000000000..dea9889d0 --- /dev/null +++ b/po/af.po @@ -0,0 +1,417 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# F Wolff , 2019 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Afrikaans (http://www.transifex.com/freedesktop/fwupd/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" + +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minuut oor" +msgstr[1] "%.0f minute oor" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dag" +msgstr[1] "%u dae" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u uur" +msgstr[1] "%u ure" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuut" +msgstr[1] "%u minute" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekonde" +msgstr[1] "%u sekondes" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Bygevoeg" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Ouderdom" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "’n Bywerking vereis dat die stelsel herbegin om te voltooi." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Antwoord ja op alle vrae" + +#. TRANSLATORS: device attributes, i.e. things that +#. * the device can do +msgid "Attributes" +msgstr "Attribute" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Kanselleer" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Gekanselleer" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Verander" + +#. TRANSLATORS: section header for firmware checksum +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrolesom" + +#. TRANSLATORS: chip ID, e.g. "0x58200204" +msgid "Chip ID" +msgstr "Vlokkie-ID" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Kies 'n toestel:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Kies 'n vrystelling:" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Opdrag nie gevind nie" + +msgid "DFU" +msgstr "DFU" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Pak tans uit…" + +#. TRANSLATORS: section header for firmware description +msgid "Description" +msgstr "Beskrywing" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Toestel bygevoeg:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Toestel verander:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Toestel verwyder:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Toestelle wat suksesvol bygewerk is:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Toestelle wat nie korrek bygewerk is nie:" + +msgid "Done!" +msgstr "Klaar!" + +#. TRANSLATORS: the first replacement is a display name +#. * e.g. "ColorHugALS" and the second and third are +#. * version numbers e.g. "1.2.3" +#, c-format +msgid "Downgrading %s from %s to %s... " +msgstr "Gradeer tans %s af vanaf %s na %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Gradeer tans %s af…" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Laai tans af…" + +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Geaktiveer" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Vee tans uit…" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Kon nie aflaai nie a.g.v. bedienerlimiet" + +#. TRANSLATORS: downloading unknown file +msgid "Fetching file" +msgstr "Kry tans lêer" + +#. TRANSLATORS: downloading new metadata file +msgid "Fetching metadata" +msgstr "Kry tans metadata" + +#. TRANSLATORS: downloading new signing file +msgid "Fetching signature" +msgstr "Kry tans handtekening" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Lêernaam" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Lêernaamhandtekening" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Gevind" + +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "ID" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Ledig…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installeer tans op %s…" + +msgid "Keyring" +msgstr "Sleutelring" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Minder as een minuut oor" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Laai tans…" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metadata-URI" + +#. TRANSLATORS: remote URI +msgid "Metadata URI Signature" +msgstr "Metadata-URI-handtekening" + +msgid "Mode" +msgstr "Modus" + +#. TRANSLATORS: interface name, e.g. "Flash" +#. TRANSLATORS: device name, e.g. 'ColorHug2' +#. TRANSLATORS: section header for the release name +msgid "Name" +msgstr "Naam" + +msgid "OK" +msgstr "Goed" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Wagwoord" + +msgid "Permission denied" +msgstr "Toestemming gewyer" + +msgid "Print the version number" +msgstr "Druk die weergawenommer" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Prioriteit" + +msgid "Proceed with upload?" +msgstr "Gaan voort met oplaai?" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Protokol" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Lees tans…" + +#. TRANSLATORS: these are areas of memory on the chip +msgid "Region" +msgstr "Streek" + +#. TRANSLATORS: the first replacement is a display name +#. * e.g. "ColorHugALS" and the second is a version number +#. * e.g. "1.2.3" +#, c-format +msgid "Reinstalling %s with %s... " +msgstr "Herinstalleer tans %s met %s… " + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Verwyder" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Internetverbinding is nodig" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Herbegin nou?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Herbegin tans toestel…" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Skeduleer die installasie vir die volgende herbegin indien moontlik" + +#. TRANSLATORS: scheduing an update to be done on the next boot +msgid "Scheduling…" +msgstr "Skeduleer tans…" + +#. TRANSLATORS: serial number, e.g. '00012345' +msgid "Serial" +msgstr "Reeksnommer" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Wys toestelle wat nie bygewerk kan word nie" + +#. TRANSLATORS: device state, i.e. appIDLE +msgid "State" +msgstr "Toestand" + +#. TRANSLATORS: probably not run as root... +#. TRANSLATORS: device has failed to report status +#. TRANSLATORS: device status, e.g. "OK" +msgid "Status" +msgstr "Status" + +#. TRANSLATORS: section header for the release one line summary +msgid "Summary" +msgstr "Opsomming" + +msgid "Target" +msgstr "Teiken" + +#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" +msgid "Title" +msgstr "Titel" + +#. TRANSLATORS: transfer size in bytes +msgid "Transfer Size" +msgstr "Oordraggrootte" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipe" + +#. TRANSLATORS: section header for firmware URI +msgid "URI" +msgstr "URI" + +#. TRANSLATORS: currect daemon status is unknown +msgid "Unknown" +msgstr "Onbekend" + +#. TRANSLATORS: section header for firmware checksum +msgid "Update Checksum" +msgstr "Bywerking se kontrolesom" + +#. TRANSLATORS: section header for long firmware desc +msgid "Update Description" +msgstr "Bywerking se beskrywing" + +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Bywerking se duur" + +#. TRANSLATORS: section header for firmware remote http:// +msgid "Update Location" +msgstr "Bywerking se ligging" + +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "Bywerking se naam" + +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "Bywerking se opsomming" + +#. TRANSLATORS: section header for firmware version +msgid "Update Version" +msgstr "Bywerking se weergawe" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Werk nou by?" + +#. TRANSLATORS: the first replacement is a display name +#. * e.g. "ColorHugALS" and the second and third are +#. * version numbers e.g. "1.2.3" +#, c-format +msgid "Updating %s from %s to %s... " +msgstr "Werk tans %s by vanaf %s na %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Werk tans %s by…" + +#. TRANSLATORS: the server sent the user a small message +msgid "Upload message:" +msgstr "Oplaaiboodskap:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Laai verslag nou op?" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Gebruikernaam" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verifieer tans…" + +#. TRANSLATORS: section header for release version number +msgid "Version" +msgstr "Weergawe" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Wag tans…" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Skryf tans…" diff --git a/po/ast.po b/po/ast.po index 163582df6..c9181d05c 100644 --- a/po/ast.po +++ b/po/ast.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Asturian (http://www.transifex.com/freedesktop/fwupd/language/ast/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -77,15 +75,6 @@ msgstr "Estáu" msgid "Status" msgstr "Estáu" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Esti proyeutu tien l'oxetivu d'anovar automáticamente'l firmware en Linux d'un mou seguru y fiable. Pues usar un xestor de software GUI como Gnome Software pa ver y aplicar los anovamientos, la ferramienta en llinia de comandos o la interfaz D-Bus direutamente." - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Tamañu de tresferencia" - -msgid "Update device firmware on Linux" -msgstr "Anueva'l firmware de preseos en Linux" - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/ca.po b/po/ca.po index 22d4b2203..3dfb1bb73 100644 --- a/po/ca.po +++ b/po/ca.po @@ -9,8 +9,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Catalan (http://www.transifex.com/freedesktop/fwupd/language/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -30,6 +28,34 @@ msgstr[1] "Manquen %.0f minuts" msgid "%s has firmware updates:" msgstr "%s té actualitzacions de microprogramari:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dia" +msgstr[1] "%u dies" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hora" +msgstr[1] "%u hores" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minut" +msgstr[1] "%u minuts" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u segon" +msgstr[1] "%u segons" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "S'ha afegit" @@ -55,7 +81,7 @@ msgstr "Permet tornar a la versió anterior del microprogramari" msgid "Allow re-installing existing firmware versions" msgstr "Permet tornar a instal·lar les versions de microprogramari existents" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Una actualització requereix un reinici per a completar-se." @@ -266,6 +292,11 @@ msgstr "Desactualitza el microprogramari en un dispositiu" msgid "Downgrading %s from %s to %s... " msgstr "S'està desactualitzant %s des de %s a %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "S'està desactualitzant %s…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "S'està descarregant..." @@ -397,6 +428,9 @@ msgstr "Les actualitzacions de microprogramari no estan admeses en aquesta màqu msgid "Firmware updates are supported on this machine." msgstr "Les actualitzacions de microprogramari estan admeses en aquesta màquina." +msgid "Force the action ignoring all warnings" +msgstr "Força l'acció ignorant tots els avisos" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "S'ha trobat" @@ -475,12 +509,6 @@ msgstr "Instal·la microprogramari sense signar per al dispositiu" msgid "Install unsigned system firmware" msgstr "Instal·la microprogramari sense signar per al sistema" -#. show message in progressbar -#. TRANSLATORS: %1 is a device name -#, c-format -msgid "Installing %s" -msgstr "S'està instal·lant %s" - #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "S'està instal·lant l'actualització de microprogramari..." @@ -597,6 +625,12 @@ msgstr "Permís denegat" msgid "Please enter a number from 0 to %u: " msgstr "Introduïu un número del 0 al %u: " +msgid "Print the version number" +msgstr "Imprimeix el número de versió" + +msgid "Print verbose debug statements" +msgstr "Imprimeix les sentències detallades de la depuració" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Prioritat" @@ -743,6 +777,10 @@ msgstr "Comparteix l'historial de microprogramari amb els desenvolupadors" msgid "Show client and daemon versions" msgstr "Mostra les versions del client i el dimoni" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "Mostra la informació detallada del dimoni" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Mostra la informació de depuració per a tots els fitxers" @@ -775,6 +813,12 @@ msgstr "Mostra el registre de depuració del darrer intent d'actualització" msgid "Show the information of firmware update status" msgstr "Mostra la informació sobre l'estat de l'actualització del microprogramari" +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifiqueu el proveïdor/ID del producte del dispositiu DFU" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifiqueu el nombre de bytes per a la transferència USB" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Estat" @@ -796,20 +840,10 @@ msgstr "Objectiu" 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 "El LVFS és un servei gratuït que funciona com una entitat legal independent i no té cap vincle amb la $OS_RELEASE:NAME$. És possible que el vostre distribuïdor no hagi verificat cap de les actualitzacions del microprogramari per a la compatibilitat amb el vostre sistema o els dispositius connectats. Tot el microprogramari només és proporcionat pel fabricant original dels equips." -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "El procés del «fwupd» és un dimoni senzill, el qual permet que una sessió de programari actualitzi el microprogramari del dispositiu a la màquina local. Està dissenyat per a equips d'escriptori, però aquest projecte també és usable a telèfons, tauletes i servidors sense perifèrics." - -#. TRANSLATORS: exactly one update needs this -msgid "The update requires a reboot to complete." -msgstr "L'actualització requereix un reinici per a completar-se." - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Aquest programa només pot funcionar correctament com a «root»" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Aquest projecte pretén fer que actualitzar automàticament el microprogramari a Linux, sigui segur i fiable. Com a IGU, podeu usar un gestor de programari com el Programari GNOME per a veure i aplicar les actualitzacions, directament l'eina de la línia d'ordres o la interfície de D-Bus." - 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 "Aquest remot conté el microprogramari que no està embargat, però encara l'ha provat el proveïdor del maquinari. Haureu d'assegurar-vos que teniu una manera de baixar manualment el microprogramari si l'actualització del microprogramari no funciona." @@ -856,6 +890,11 @@ msgstr "Suma de verificació de l'actualització" msgid "Update Description" msgstr "Descripció de l'actualització" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Durada de l'actualització" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Ubicació de l'actualització" @@ -876,9 +915,6 @@ msgstr "Actualitza el resum" msgid "Update Version" msgstr "Versió de l'actualització" -msgid "Update device firmware on Linux" -msgstr "Actualitza el microprogramari del dispositiu a Linux" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Que falli en actualitzar és un problema conegut, visiteu aquest URL per obtenir més informació:" @@ -905,6 +941,11 @@ msgstr "Actualitza tot el microprogramari a les versions més recents" msgid "Updating %s from %s to %s... " msgstr "S'està actualitzant %s des de %s a %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "S'està actualitzant %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Missatge de la pujada:" @@ -956,6 +997,3 @@ msgstr "S'està escrivint..." #. 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 "És possible que el vostre distribuïdor no hagi verificat cap de les actualitzacions del microprogramari per a la compatibilitat amb el vostre sistema o els dispositius connectats." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/cs.po b/po/cs.po index 7908c6658..d8e467eac 100644 --- a/po/cs.po +++ b/po/cs.po @@ -3,15 +3,13 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Daniel Rusek , 2017 -# Daniel Rusek , 2017 +# Ascii Wolf , 2017 +# Ascii Wolf , 2017 # Marek Černocký , 2016,2018 msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Czech (http://www.transifex.com/freedesktop/fwupd/language/cs/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -58,7 +56,7 @@ msgstr "Povolit přechod na nižší verze firmwaru" msgid "Allow re-installing existing firmware versions" msgstr "Povolit reinstalaci stávající verze firmwaru" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Některá z aktualizací vyžaduje pro dokončení restart." @@ -480,12 +478,6 @@ msgstr "Instalace nepodepsaného firmwaru zařízení" msgid "Install unsigned system firmware" msgstr "Instalace nepodepsaného systémového firmwaru" -#. show message in progressbar -#. TRANSLATORS: %1 is a device name -#, c-format -msgid "Installing %s" -msgstr "Instaluje se zařízení %s" - #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Instaluje se aktualizace firmwaru…" @@ -801,20 +793,10 @@ msgstr "Cíl" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS je svobodná služba, které funguje jako nezávislý právní subjekt a nemá žádné vazby na systém $OS_RELEASE:NAME$. Váš distributor nemusí některé z aktualizací firmwaru schválit kvůli kompatibilitě s vaším systémem nebo připojenými zařízeními. Veškerý firmware je poskytován pouze přímo výrobci daných zařízení." -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Proces fwupd je jednoduchý démon, který umožňuje softwaru sezení aktualizovat firmware zařízení na vašem počítači. Je navržen pro stolní počítače, ale je možné jej používat i na telefonech, tabletech a serverech bez monitoru." - -#. TRANSLATORS: exactly one update needs this -msgid "The update requires a reboot to complete." -msgstr "Pro dokončení aktualizace je potřeba provést restart." - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Tento program může správně fungovat jen pod uživatelem root" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Cílem tohoto projektu je, aby aktualizace firmwaru na Linuxu probíhala automaticky, bezpečně a spolehlivě. Pro zobrazení a nasazení aktualizací můžete použít správce softwaru s grafickým rozhraním, jako je třeba Software GNOME, nebo nástroj pro příkazovou řádku či přímo rozhraní D-Bus." - msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Tento zdroj obsahuje firmware, který není zakázaný, ale zatím je u výrobce stále ve stádiu testování. Měli byste se ujistit, že znáte způsob, jak se vrátit k předchozí verzi firmwaru, kdyby došlo k selhání." @@ -881,9 +863,6 @@ msgstr "Souhrn aktualizace" msgid "Update Version" msgstr "Verze aktualizace" -msgid "Update device firmware on Linux" -msgstr "Aktualizace firmwaru zařízení na Linuxu" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "O selhání aktualizace se ví, více informací najdete na této adrese:" @@ -961,6 +940,3 @@ msgstr "Zapisuje se…" #. 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 "Váš distributor nemusí schválit některé aktualizace firmwaru kvůli kompatibilitě s vaším systémem nebo připojenými zařízeními." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/de.po b/po/de.po index b34a04dad..6c59a74d3 100644 --- a/po/de.po +++ b/po/de.po @@ -3,14 +3,13 @@ # This file is distributed under the same license as the fwupd package. # # Translators: +# Ettore Atalan , 2018 # Marco Tedaldi , 2015 # Wolfgang Stöggl , 2015 msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: German (http://www.transifex.com/freedesktop/fwupd/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,15 +17,26 @@ msgstr "" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f Minute verbleibt" +msgstr[1] "%.0f Minuten verbleiben" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" -msgstr "Firmwareaktualisierungen für %s verfügbar:" +msgstr "Firmware-Aktualisierungen für %s verfügbar:" #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Hinzugefügt" +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Alter" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" @@ -34,23 +44,48 @@ msgstr "Verweis auf %s" #. TRANSLATORS: command line option msgid "Allow downgrading firmware versions" -msgstr "Einspielen niedrigerer Firmwareversionen zulassen (Downgrade)" +msgstr "Herabstufung von Firmware-Versionen zulassen" #. TRANSLATORS: command line option msgid "Allow re-installing existing firmware versions" msgstr "Erneute Installation vorhandener Firmware-Versionen erlauben" +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Ein Neustart ist erforderlich, um eine Aktualisierung abzuschließen." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Alle Fragen mit Ja beantworten" + +#. TRANSLATORS: command description +msgid "Apply a binary patch" +msgstr "Binären Patch anwenden" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Firmware-Aktualisierungen anwenden" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "DFU-fähiges Gerät wieder zurück in Laufzeit einhängen" +#. TRANSLATORS: device attributes, i.e. things that +#. * the device can do +msgid "Attributes" +msgstr "Attribute" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Authentifizierung …" + #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "Legitimierung ist erforderlich, um das Firmware-Downgrade auf einem entfernbaren Gerät durchzuführen" +msgstr "Für eine Herabstufung der Firmware auf einem entfernbaren Gerät ist eine Authentifizierung erforderlich" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" -msgstr "Auf diesem System ist eine Legitimierung erforderlich, um das Firmware-Downgrade durchzuführen" +msgstr "Für eine Herabstufung der Firmware auf diesem System ist eine Authentifizierung erforderlich" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" @@ -58,11 +93,23 @@ msgstr "Legitimation ist zum Entsperren eines Geräts erforderlich" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on a removable device" -msgstr "Legitimierung ist notwendig, um die Firmware auf einem entfernbaren Gerät zu aktualisieren" +msgstr "Für die Aktualisierung der Firmware auf einem entfernbaren Gerät ist eine Authentifizierung erforderlich" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" -msgstr "Auf diesem System ist eine Authentifizierung notwendig, um das Firmware Update durch zu führen" +msgstr "Für die Aktualisierung der Firmware auf diesem System ist eine Authentifizierung erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Eine Authentifizierung ist erforderlich, um die gespeicherten Prüfsummen für das Gerät zu aktualisieren" + +#. TRANSLATORS: command description +msgid "Build firmware using a sandbox" +msgstr "Firmware mit Hilfe einer Sandbox erstellen" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Abbrechen" #. TRANSLATORS: this is when a device ctrl+c's a watch msgid "Cancelled" @@ -78,6 +125,18 @@ msgstr "Geändert" msgid "Checksum" msgstr "Prüfsumme" +#. TRANSLATORS: chip ID, e.g. "0x58200204" +msgid "Chip ID" +msgstr "Chip-Kennung" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Wählen Sie ein Gerät aus:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Wählen Sie eine Version aus:" + #. TRANSLATORS: this is the encryption method used when writing msgid "Cipher" msgstr "Verschlüsselungsverfahren" @@ -94,9 +153,16 @@ msgstr "Befehl nicht gefunden" msgid "Convert firmware to DFU format" msgstr "Firmware in das DFU-Format konvertieren" +#. TRANSLATORS: command description +msgid "Create a binary patch using two files" +msgstr "Binären Patch aus zwei Dateien erstellen" + +msgid "DFU" +msgstr "DFU" + #. TRANSLATORS: DFU stands for device firmware update msgid "DFU Utility" -msgstr "DFU-Werkzeug" +msgstr "DFU-Dienstprogramm" #. TRANSLATORS: for the --verbose arg msgid "Debugging Options" @@ -108,7 +174,7 @@ msgstr "Entpacken …" #. TRANSLATORS: command description msgid "Decrypt firmware data" -msgstr "Firmwaredaten entschlüsseln" +msgstr "Firmware-Daten entschlüsseln" #. TRANSLATORS: section header for firmware description msgid "Description" @@ -130,23 +196,101 @@ msgstr "Gerät geändert:" msgid "Device removed:" msgstr "Gerät entfernt:" +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Erfolgreich aktualisierte Geräte:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Nicht korrekt aktualisierte Geräte:" + +msgid "Disabled fwupdate debugging" +msgstr "fwupdate-Defektlokalisierung deaktivieren" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Version anzeigen" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Nicht auf alte Metadaten prüfen" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Nach der Aktualisierung nicht auf einen Neustart prüfen" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Nicht auf nicht erfassten Verlauf prüfen" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Nicht in die Verlaufsdatenbank schreiben" + msgid "Done!" msgstr "Fertig." +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Stuft die Firmware auf einem Gerät herab" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" #, c-format msgid "Downgrading %s from %s to %s... " -msgstr "Downgrade für %s von %s auf %s wird eingespielt …" +msgstr "Herabstufung für %s von %s auf %s wird eingespielt…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "%s wird herabgestuft …" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Herunterladen …" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "SMBIOS-Daten aus einer Datei ausgeben" #. TRANSLATORS: command description msgid "Dump details about a firmware file" msgstr "Details zu einer Firmware-Datei ausgeben" +#. TRANSLATORS: command description +msgid "Dump information about a binary patch to the screen" +msgstr "Informationen über einen binären Patch auf dem Bildschirm ausgeben" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Das angegebene ESP war nicht gültig" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Firmware-Aktualisierungsunterstützung auf unterstützten Systemen aktivieren" + +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Aktiviert" + +msgid "Enabled fwupdate debugging" +msgstr "fwupdate-Defektlokalisierung aktivieren" + +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 "Die Aktivierung dieser Funktionalität erfolgt auf eigene Gefahr, d.h. Sie müssen sich bei Problemen, die durch diese Aktualisierungen verursacht werden, an Ihren Erstausrüster wenden. Nur Probleme mit dem Aktualisierungsprozess selbst sollten unter $OS_RELEASE:BUG_REPORT_URL$ eingereicht werden." + #. TRANSLATORS: command description msgid "Encrypt firmware data" -msgstr "Firmwaredaten verschlüsseln" +msgstr "Firmware-Daten verschlüsseln" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Gesamten Firmware-Aktualisierungsverlauf löschen" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Löschen …" #. TRANSLATORS: exit after we've started up, used for user profiling msgid "Exit after a small delay" @@ -160,17 +304,61 @@ msgstr "Nach dem Laden der Engine beenden" msgid "Failed to parse arguments" msgstr "Verarbeitung der Argumente schlug fehl" +#. TRANSLATORS: downloading unknown file +msgid "Fetching file" +msgstr "Datei wird abgerufen" + +#. TRANSLATORS: downloading new firmware file +msgid "Fetching firmware" +msgstr "Firmware wird abgerufen" + +#. TRANSLATORS: downloading new metadata file +msgid "Fetching metadata" +msgstr "Metadaten werden abgerufen" + +#. TRANSLATORS: downloading new signing file +msgid "Fetching signature" +msgstr "Signatur wird abgerufen" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Dateiname" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Signatur des Dateinamens" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Firmware Basis-URI" + #. TRANSLATORS: program summary msgid "Firmware Update D-Bus Service" msgstr "D-Bus-Dienst für Firmware-Aktualisierung" #. TRANSLATORS: program name msgid "Firmware Update Daemon" -msgstr "Dienst für Firmware-Aktualisierung" +msgstr "Hintergrundprogramm für Firmware-Aktualisierung" #. TRANSLATORS: program name msgid "Firmware Utility" -msgstr "Firmware-Werkzeug" +msgstr "Firmware-Dienstprogramm" + +#. 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] "Firmware-Metadaten wurden seit %u Tag nicht aktualisiert und sind möglicherweise nicht auf dem neuesten Stand." +msgstr[1] "Firmware-Metadaten wurden seit %u Tagen nicht aktualisiert und sind möglicherweise nicht auf dem neuesten Stand." + +msgid "Firmware updates are not supported on this machine." +msgstr "Firmware-Aktualisierungen werden auf diesem System nicht unterstützt." + +msgid "Firmware updates are supported on this machine." +msgstr "Firmware-Aktualisierungen werden auf diesem System unterstützt." + +msgid "Force the action ignoring all warnings" +msgstr "Aktion erzwingen, bei der alle Warnungen ignoriert werden" #. TRANSLATORS: detected a DFU device msgid "Found" @@ -179,10 +367,18 @@ msgstr "Gefunden" msgid "GUID" msgstr "GUID" +#. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Ermittelt alle Geräte gemäß der Systemtopologie" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Alle Geräte ermitteln, die Firmware-Aktualisierungen unterstützen" +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Alle aktivierten und im System registrierten Plugins ermitteln" + #. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "Ermittelt Details über eine Firmware-Datei" @@ -195,6 +391,10 @@ msgstr "Ermittelt den kryptographischen Hash-Wert der abgelegten Firmware" msgid "Gets the list of updates for connected hardware" msgstr "Ermittelt die Liste der Aktualisierungen für angeschlossene Hardware" +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Ermittelt die Versionen für ein Gerät" + #. TRANSLATORS: command description msgid "Gets the results from the last update" msgstr "Ermittelt die Ergebnisse der letzten Aktualisierung" @@ -207,6 +407,10 @@ msgstr "Kennung" msgid "Idle…" msgstr "Bereit …" +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Firmware-Blob auf einem Gerät installieren" + #. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Eine Firmware-Datei auf dieser Hardware installieren" @@ -230,10 +434,36 @@ msgstr "Nicht-signierte Geräte-Firmware installieren" msgid "Install unsigned system firmware" msgstr "Nicht-signierte System-Firmware installieren" +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Firmware-Aktualisierung wird installiert …" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Wird auf %s installiert …" + +msgid "Keyring" +msgstr "Schlüsselbund" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Weniger als eine Minute verbleiben" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (stabile Firmware)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (Test-Firmware)" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "Derzeit angeschlossene DFU-fähige Geräte auflisten" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Unterstützte Firmware-Aktualisierungen auflisten" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Laden …" @@ -242,12 +472,24 @@ msgstr "Laden …" msgid "Merge multiple firmware files into one" msgstr "Mehrere Firmware-Dateien in eine zusammenführen" +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metadaten-URI" + +#. TRANSLATORS: remote URI +msgid "Metadata URI Signature" +msgstr "Metadaten URI-Signatur" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadaten können über den Linux Vendor Firmware Service bezogen werden." + msgid "Mode" msgstr "Modus" #. TRANSLATORS: command description msgid "Monitor the daemon for events" -msgstr "Den Daemon auf Ereignisse überwachen" +msgstr "Hintergrundprogramm auf Ereignisse überwachen" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' @@ -255,18 +497,65 @@ msgstr "Den Daemon auf Ereignisse überwachen" msgid "Name" msgstr "Name" +msgid "No action specified!" +msgstr "Keine Aktion angegeben!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Es wurde keine Hardware erkannt, deren Firmware aktualisiert werden kann" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Keine Plugins gefunden" + msgid "OK" msgstr "Ok" +#. TRANSLATORS: command line option +msgid "Override plugin warning" +msgstr "Plugin-Warnung überschreiben" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Standard-ESP-Pfad überschreiben" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Passwort" + +msgid "Payload" +msgstr "Nutzdaten" + +msgid "Permission denied" +msgstr "Berechtigung verweigert" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Bitte geben Sie eine Zahl von 0 bis %u ein: " + +msgid "Print the version number" +msgstr "Versionsnummer ausgeben" + +msgid "Print verbose debug statements" +msgstr "Ausführliche Debug-Anweisungen ausgeben" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Priorität" + +msgid "Proceed with upload?" +msgstr "Mit dem Hochladen fortfahren?" + #. TRANSLATORS: DFU protocol version, e.g. 1.1 msgid "Protocol" msgstr "Protokoll" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Abfrage der Unterstützung für Firmware-Aktualisierungen" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -280,6 +569,10 @@ msgstr "Firmware von Gerät in Datei schreiben" msgid "Read firmware from one partition into a file" msgstr "Firmware von einzelner Partition in Datei lesen" +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Lesen …" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Metadaten von entferntem Server aktualisieren" @@ -299,10 +592,41 @@ msgstr "Erneute Installation von %s mit %s …" msgid "Removed" msgstr "Entfernt" +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Daten in einer bestehenden Firmware-Datei ersetzen" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Bericht-URI" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Erfordert Internetverbindung" + +#. TRANSLATORS: command description +msgid "Reset a DFU device" +msgstr "DFU-Gerät zurücksetzen" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Jetzt neu starten?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Gerät wird neu gestartet …" +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Alle Hardware-Kennungen für das System zurückgeben" + +msgid "Runtime" +msgstr "Laufzeit" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Installation für den nächsten Neustart planen, falls möglich" + #. TRANSLATORS: scheduing an update to be done on the next boot msgid "Scheduling…" msgstr "Einplanen …" @@ -333,7 +657,7 @@ msgstr "Veröffentlichungsversion einer Firmware-Datei festlegen" #. TRANSLATORS: command description msgid "Set the firmware size for the target" -msgstr "Die Firmware-Größe für das Ziel festlegen" +msgstr "Firmware-Größe für das Ziel festlegen" #. TRANSLATORS: command description msgid "Set vendor ID on firmware file" @@ -343,6 +667,18 @@ msgstr "Hersteller-Kennung einer Firmware-Datei festlegen" msgid "Sets metadata on a firmware file" msgstr "Metadaten einer Firmware-Datei festlegen" +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Firmware-Verlauf mit den Entwicklern teilen" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Client- und Hintergrundprogramm-Versionen anzeigen" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "Ausführliche Informationen zum Dienstprogramm anzeigen" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Debuginformationen für alle Dateien anzeigen" @@ -351,10 +687,33 @@ msgstr "Debuginformationen für alle Dateien anzeigen" msgid "Show debugging options" msgstr "Debug Optionen anzeigen" +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Nicht aktualisierbare Geräte anzeigen" + #. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Zusätzliche Informationen zur Fehlerdiagnose anzeigen" +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Verlauf von Firmware-Aktualisierungen anzeigen" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Ausführliche Informationen zum Plugin anzeigen" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Fehlerprotokoll der letzten versuchten Aktualisierung anzeigen" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Informationen über den Firmware-Aktualisierungsstatus anzeigen" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Geben Sie die Anzahl der Bytes pro USB-Übertragung an" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Zustand" @@ -365,6 +724,41 @@ msgstr "Zustand" msgid "Status" msgstr "Status" +#. TRANSLATORS: section header for the release one line summary +msgid "Summary" +msgstr "Zusammenfassung" + +msgid "Target" +msgstr "Ziel" + +#. 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 "Der LVFS ist ein kostenloser Dienst, der als unabhängige juristische Person arbeitet und keine Verbindung zu $OS_RELEASE:NAME$ hat. Möglicherweise hat Ihr Lieferant eine der Firmware-Aktualisierungen nicht auf Kompatibilität mit Ihrem System oder angeschlossenen Geräten überprüft. Die gesamte Firmware wird nur vom Originalhersteller zur Verfügung gestellt." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Dieses Programm funktioniert möglicherweise nur als root korrekt" + +#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" +msgid "Title" +msgstr "Titel" + +#. TRANSLATORS: transfer size in bytes +msgid "Transfer Size" +msgstr "Übertragungsgröße" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Typ" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI-Firmware-Dienstprogramm" + +#. TRANSLATORS: section header for firmware URI +msgid "URI" +msgstr "URI" + #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Unbekannt" @@ -388,10 +782,29 @@ msgstr "Beschreibung aktualisieren" msgid "Update Location" msgstr "Ort aktualisieren" +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "Aktualisierungsname" + +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "Aktualisierungszusammenfassung" + #. TRANSLATORS: section header for firmware version msgid "Update Version" msgstr "Version aktualisieren" +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Der Aktualisierungsfehler ist ein bekanntes Problem, besuchen Sie diese URL für weitere Informationen:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Jetzt aktualisieren?" + +msgid "Update the stored device verification information" +msgstr "Gespeicherte Geräteverifizierungsinformationen aktualisieren" + #. TRANSLATORS: command description msgid "Update the stored metadata with current ROM contents" msgstr "Gespeicherte Metadaten mit dem aktuellen ROM-Inhalt aktualisieren" @@ -407,18 +820,47 @@ msgstr "Alle Firmware auf die neueste verfügbare Version aktualisieren" msgid "Updating %s from %s to %s... " msgstr "Aktualisieren von %s von %s nach %s …" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s wird aktualisiert …" + +#. TRANSLATORS: the server sent the user a small message +msgid "Upload message:" +msgstr "Nachricht beim Hochladen:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Bericht jetzt hochladen?" + +#. 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 "Das Hochladen von Firmware-Berichten hilft Hardwareherstellern, fehlerhafte und erfolgreiche Aktualisierungen auf realen Geräten schnell zu erkennen." + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Benutzername" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" -msgstr "Überprüfung läuft …" +msgstr "Überprüfung …" #. TRANSLATORS: section header for release version number msgid "Version" msgstr "Version" +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Warten …" + #. TRANSLATORS: command description msgid "Watch DFU devices being hotplugged" msgstr "Geräteanschluss von DFU-Geräten überwachen" +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Auf Hardware-Änderungen achten" + #. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "Firmware von Datei auf Gerät schreiben" @@ -430,3 +872,7 @@ msgstr "Firmware aus Datei in einzelne Partition schreiben" #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Schreiben …" + +#. 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 "Möglicherweise hat Ihr Lieferant eine der Firmware-Aktualisierungen nicht auf Kompatibilität mit Ihrem System oder angeschlossenen Geräten überprüft." diff --git a/po/en_GB.po b/po/en_GB.po index c7238cd4a..2f1e41160 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: English (United Kingdom) (http://www.transifex.com/freedesktop/fwupd/language/en_GB/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -609,12 +607,6 @@ msgstr "Summary" msgid "Target" msgstr "Target" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." - -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." - #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" msgstr "Title" @@ -670,9 +662,6 @@ msgstr "Update Summary" msgid "Update Version" msgstr "Update Version" -msgid "Update device firmware on Linux" -msgstr "Update device firmware on Linux" - #. TRANSLATORS: ask the user if we can update the metadata msgid "Update now?" msgstr "Update now?" @@ -730,6 +719,3 @@ msgstr "Write firmware from file into one partition" #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Writing…" - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/eu.po b/po/eu.po index 685a0c25b..5614fddec 100644 --- a/po/eu.po +++ b/po/eu.po @@ -8,9 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-06-06 14:54+0100\n" -"PO-Revision-Date: 2018-06-06 14:00+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Basque (http://www.transifex.com/freedesktop/fwupd/language/eu/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/fi.po b/po/fi.po index 0e91f0a40..6cd7645c1 100644 --- a/po/fi.po +++ b/po/fi.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Finnish (http://www.transifex.com/freedesktop/fwupd/language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -179,9 +177,6 @@ msgstr "Tyyppi" msgid "Unknown" msgstr "Tuntematon" -msgid "Update device firmware on Linux" -msgstr "Päivitä laitteiden firmware-laiteohjelmistoja Linuxilla" - #. TRANSLATORS: remote filename base msgid "Username" msgstr "Käyttäjätunnus" @@ -201,6 +196,3 @@ msgstr "Odotetaan…" #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Kirjoitetaan…" - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/fr.po b/po/fr.po index bec71a018..21026afb2 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: French (http://www.transifex.com/freedesktop/fwupd/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/fur.po b/po/fur.po index e7857169c..489c19207 100644 --- a/po/fur.po +++ b/po/fur.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Friulian (http://www.transifex.com/freedesktop/fwupd/language/fur/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -43,7 +41,7 @@ msgstr "Permet di tornâ indaûr aes versions di firmware precedentis" msgid "Allow re-installing existing firmware versions" msgstr "Permet di tornâ a instalâ lis versions dai firmware esistentis" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Un inzornament al à bisugne che si torni a inviâ il computer par finî." @@ -561,9 +559,6 @@ msgstr "Stât" msgid "Summary" msgstr "Sintesi" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Il procès fwupd al è un sempliç demoni par permeti al software de session di inzornâ i firmware dai dispositîfs su la machine locâl. Al è progjetât pai scritoris, ma chest progjet si pues doprâ ancje sui telefonins, tablet e sui servidôrs cence visôr." - #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" msgstr "Titul" @@ -615,9 +610,6 @@ msgstr "Inzorne sintesi" msgid "Update Version" msgstr "Inzorne version" -msgid "Update device firmware on Linux" -msgstr "Inzorne il firmware dal dispositîf su Linux" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Il faliment dal inzornament al è un probleme cognossût, visite chest URL par vê plui informazions:" @@ -671,6 +663,3 @@ msgstr "Scrîf il firmware dal file intune partizion" #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Daûr a scrivi…" - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/he.po b/po/he.po index dd821c89a..e3fff2455 100644 --- a/po/he.po +++ b/po/he.po @@ -9,8 +9,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Hebrew (http://www.transifex.com/freedesktop/fwupd/language/he/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/hi.po b/po/hi.po index 8a339b22e..6cb53320c 100644 --- a/po/hi.po +++ b/po/hi.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Hindi (http://www.transifex.com/freedesktop/fwupd/language/hi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/hr.po b/po/hr.po index a01d18f0d..d4f773c99 100644 --- a/po/hr.po +++ b/po/hr.po @@ -10,8 +10,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Croatian (http://www.transifex.com/freedesktop/fwupd/language/hr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -45,7 +43,7 @@ msgstr "Dopusti vraćanje starije inačice frimvera" msgid "Allow re-installing existing firmware versions" msgstr "Dopusti ponovnu instalaciju frimvera postojeće inačice" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Nadopuna zahtijeva ponovno pokretanje za završetak." @@ -637,12 +635,6 @@ msgstr "Sažetak" msgid "Target" msgstr "Odredište" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "fwupd je jednostavan pozadinski program koji omogućuje softveru sesije nadopunu frimvera uređaja na vašem lokalnom računalu. Dizajniran je za stolna računala, ali ovaj projekt se može koristiti i na mobilnim telefonima, tabletima i poslužiteljima." - -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Svrha ovog projekta je automatsko, sigurno i pouzdano nadopunjivanje frimvera na linuxu. Kako bi vidjeli i primijenili nadopune frimvera možete koristiti upravitelja softverom poput GNOME Softvera, alat naredbenog redka ili izravno D-Bus sučelje." - #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" msgstr "Naziv" @@ -698,9 +690,6 @@ msgstr "Sažetak nadopune" msgid "Update Version" msgstr "Inačica nadopune" -msgid "Update device firmware on Linux" -msgstr "Nadopunite frimvere uređaja na Linuxu" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Neuspješna nadopuna je poznat problem, posjetite ovaj URL za više informacija:" @@ -766,6 +755,3 @@ msgstr "Zapiši frimver iz datoteke u jednu particiju uređaja" #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Zapisivanje..." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/hu.po b/po/hu.po index 1aa02c837..5d8b4dccf 100644 --- a/po/hu.po +++ b/po/hu.po @@ -11,8 +11,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Hungarian (http://www.transifex.com/freedesktop/fwupd/language/hu/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,11 +18,46 @@ msgstr "" "Language: hu\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f perc van hátra" +msgstr[1] "%.0f perc van hátra" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s firmware frissítésekkel rendelkezik:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u nap" +msgstr[1] "%u nap" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u óra" +msgstr[1] "%u óra" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u perc" +msgstr[1] "%u perc" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u másodperc" +msgstr[1] "%u másodperc" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Hozzáadva" @@ -33,6 +66,10 @@ msgstr "Hozzáadva" msgid "Age" msgstr "Kor" +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Beleegyezik és engedélyezi a távoli tárolót?" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" @@ -46,7 +83,7 @@ msgstr "Firmware verziók visszafejlesztésének engedélyezése" msgid "Allow re-installing existing firmware versions" msgstr "Meglévő firmware verziók újratelepítésének engedélyezése" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Egy frissítés újraindítást igényel a befejezéshez." @@ -58,10 +95,18 @@ msgstr "Igen az összes kérdésre" msgid "Apply a binary patch" msgstr "Bináris folt alkalmazása" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Firmware-frissítések alkalmazása" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "DFU-képes eszköz visszacsatolása a futtatókörnyezethez" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Csatlakoztatás a firmware módhoz" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -184,6 +229,10 @@ msgstr "Leírás" msgid "Detach currently attached DFU capable device" msgstr "Jelenleg csatlakoztatott DFU-képes eszközök leválasztása" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Leválasztás a indítóbetöltő módhoz" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Eszköz hozzáadva:" @@ -204,6 +253,17 @@ msgstr "Eszközök, melyek sikeresen frissítve lettek:" msgid "Devices that were not updated correctly:" msgstr "Eszközök, melyek nem lettek helyesen frissítve:" +msgid "Disabled fwupdate debugging" +msgstr "Fwupdate hibakeresés letiltva" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Letiltja az adott távoli tárolót" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Verzió megjelenítése" + #. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Ne ellenőrizze a régi metaadatokat" @@ -216,6 +276,10 @@ msgstr "Ne ellenőrizze az újraindítást a frissítés után" msgid "Do not check for unreported history" msgstr "Ne ellenőrizze a nem jelentett előzményeket" +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Ne írjon az előzmények adatbázisába" + msgid "Done!" msgstr "Kész!" @@ -230,6 +294,11 @@ msgstr "A firmware visszafejlesztése az eszközön" msgid "Downgrading %s from %s to %s... " msgstr "%s visszafejlesztése: %s -> %s…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "%s visszaállítása…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Letöltés…" @@ -246,10 +315,36 @@ msgstr "Részletek kiírása egy firmware fájlról" msgid "Dump information about a binary patch to the screen" msgstr "Információk kiírása a képernyőre a bináris foltról" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "A megadott ESP érvénytelen" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Firmware frissítési támogatás engedélyezése a támogatott rendszereken" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Engedélyezi ezt a távoli tárolót?" + #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Engedélyezve" +msgid "Enabled fwupdate debugging" +msgstr "Fwupdate hibakeresés engedélyezve" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Engedélyezi az adott távoli tárolót" + +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 "Csak a saját felelősségére engedélyezze ezt a funkciót, amely azt jelenti, hogy az eredeti termék gyártójával kell kapcsolatba lépnie, ha problémát okoz a frissítés. Csak a frissítési folyamattal kapcsolatos problémákat jelentse itt be: $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "A saját felelősségére engedélyezze ezt a távoli tárolót." + #. TRANSLATORS: command description msgid "Encrypt firmware data" msgstr "Firmware adatok titkosítása" @@ -270,6 +365,10 @@ msgstr "Kilépés egy kis késleltetés után" msgid "Exit after the engine has loaded" msgstr "Kilépés a motor betöltődése után" +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "A letöltés kiszolgálókorlát miatt meghiúsult" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "A trükkök betöltése sikertelen" @@ -325,6 +424,15 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "A firmware metaadatok %u napja nem lettek frissítve, és lehet hogy elavultak." msgstr[1] "A firmware metaadatok %u napja nem lettek frissítve, és lehet hogy elavultak." +msgid "Firmware updates are not supported on this machine." +msgstr "A firmware frissítések nem támogatottak ezen a gépen." + +msgid "Firmware updates are supported on this machine." +msgstr "A firmware frissítések támogatottak ezen a gépen." + +msgid "Force the action ignoring all warnings" +msgstr "A művelet erőltetése, az összes figyelmeztetés mellőzése" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Megtalálva" @@ -332,10 +440,18 @@ msgstr "Megtalálva" msgid "GUID" msgstr "GUID" +#. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Az összes eszköz lekérdezése a rendszer topológiájának megfelelően" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Minden eszköz lekérése, amelyek támogatják a firmware frissítéseket" +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Az összes rendszeren regisztrált és engedélyezett bővítmény lekérdezése" + #. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "Részleteket kér le egy firmware fájlról" @@ -368,6 +484,10 @@ msgstr "Azonosító" msgid "Idle…" msgstr "Üresjárat…" +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Firmware blob telepítése egy eszközre" + #. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Egy firmware fájl telepítése ezen a hardveren" @@ -395,17 +515,40 @@ msgstr "Nem aláírt rendszer firmware telepítése" msgid "Installing firmware update…" msgstr "Firmware frissítés telepítése…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "%s telepítése…" + msgid "Keyring" msgstr "Kulcstartó" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Kevesebb mint egy perc van hátra" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux gyártói firmware szolgáltatás (stabil firmware)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux gyártói firmware szolgáltatás (teszt firmware)" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "Jelenleg csatlakoztatott DFU-képes eszközök felsorolása" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "A támogatott firmware-frissítések listázása" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Betöltés…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Egyes bővítmények kézi fehérlistára tétele" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "Több firmware fájl egyesítése" @@ -418,6 +561,10 @@ msgstr "Metaadat URI" msgid "Metadata URI Signature" msgstr "Metaadat URI aláírása" +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "A metaadatok nem szerezhetőek be a Linux gyártói firmware szolgáltatásból." + msgid "Mode" msgstr "Mód" @@ -438,11 +585,22 @@ msgstr "A démon eseményeinek figyelése" msgid "Name" msgstr "Név" +msgid "No action specified!" +msgstr "Nincs művelet megadva!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nem észlelhető firmware frissítési képességgel rendelkező hardver" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nem található bővítmény" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Jelenleg nincsenek engedélyezett távoli tárolók, így nem érhetőek el metaadatok." + msgid "OK" msgstr "OK" @@ -450,6 +608,10 @@ msgstr "OK" msgid "Override plugin warning" msgstr "Bővítmény figyelmeztetés felülbírálása" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Az alapértelmezett ESP útvonal felülbírálása" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Jelszó" @@ -465,6 +627,12 @@ msgstr "Hozzáférés megtagadva" msgid "Please enter a number from 0 to %u: " msgstr "Adjon meg egy számot 0 és %u között:" +msgid "Print the version number" +msgstr "A verziószám kiírása." + +msgid "Print verbose debug statements" +msgstr "Részletes hibakeresési utasítások kiírása" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Prioritás" @@ -476,6 +644,10 @@ msgstr "Folytatja a feltöltést?" msgid "Protocol" msgstr "Protokoll" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Firmware frissítési támogatás lekérdezése" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -546,7 +718,7 @@ msgstr "Eszköz újraindítása…" #. TRANSLATORS: command description msgid "Return all the hardware IDs for the machine" -msgstr "A géphez tartoó összes hardverazonosító visszaadása" +msgstr "A géphez tartozó összes hardverazonosító visszaadása" msgid "Runtime" msgstr "Futtatókörnyezet" @@ -583,6 +755,10 @@ msgstr "Termékazonosító beállítása a firmware fájlon" msgid "Set release version on firmware file" msgstr "Kiadási verzió beállítása a firmware fájlon" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "A hibakeresési jelző beállítása frissítéskor" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "A firmware méretének beállítása a célhoz" @@ -603,6 +779,10 @@ msgstr "Firmware frissítése előzmények megosztása a fejlesztőkkel" msgid "Show client and daemon versions" msgstr "Ügyfél és démon verziók megjelenítése" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "A démon részletes információinak megjelenítése" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Hibakeresési információk megjelenítése minden fájlnál" @@ -611,6 +791,10 @@ msgstr "Hibakeresési információk megjelenítése minden fájlnál" msgid "Show debugging options" msgstr "Hibakeresési beállítások megjelenítése" +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Eszközök, melyek nem frissíthetőek" + #. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "További hibakeresési információk megjelenítése" @@ -623,6 +807,20 @@ msgstr "Firmware frissítési előzmények megtekintése" msgid "Show plugin verbose information" msgstr "Bővítmény bőbeszédű információinak megjelenítése" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "A legutóbb megpróbált frissítés hibakeresési naplójának megjelenítése" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "A firmware frissítési állapot információinak megjelenítése" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Adja meg a DFU eszköz gyártó-/termékazonosítóját" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Adja meg az USB átvitelek bájtjainak számát" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Állapot" @@ -640,11 +838,16 @@ msgstr "Összegzés" msgid "Target" msgstr "Cél" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "A fwupd folyamat egy egyszerű démon, amely lehetővé teszi más szoftvereknek a számítógép eszközeinek firmware-jének frissítését. Asztali számítógépekre lett tervezve, de használható telefonokon, táblagépeken és képernyő nélküli kiszolgálókon is. " +#. 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 "Az LVFS egy ingyenes szolgáltatás, amely független jogi entitásként működik, és nincs kapcsolata a $OS_RELEASE:NAME$ operációs rendszerrel. A disztribúció szállítója nem biztos, hogy ellenőrízte kompatibilitási szempontból a firmware frissítést. Mindent firmware-t csak az eredeti termék gyártója biztosít." -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "A projekt célja a firmwarek frissítését automatikussá, biztonságossá és megbízhatóvá tétele Linuxon. Használhat grafikus szoftverkezelőket, mint a GNOME Szoftverek a frissítések megtekintéséhez és alkalmazásához, használhatja a parancssoros eszközt, vagy közvetlenül a D-Bus interfészt." +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Ez a program jelenleg lehet, hogy csak rendszergazdaként működik" + +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 "Ez a távoli tároló olyan firmware-t tartalmaz, amelyre nem vonatkozik embargó, de még teszteli a harvergyártó. Érdemes biztosítani a firmware kézi visszaállításáról, ha a firmware frissítése meghiúsul." #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -658,6 +861,10 @@ msgstr "Átviteli méret" msgid "Type" msgstr "Típus" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI firmware segédprogram" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -673,6 +880,10 @@ msgstr "Eszköz feloldása hozzáférés engedélyezéséhez" msgid "Unlocks the device for firmware access" msgstr "Eszköz feloldása a firmware eléréséhez" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "A hibakeresési jelző kikapcsolása frissítéskor" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Frissítés ellenőrzőösszege" @@ -681,6 +892,11 @@ msgstr "Frissítés ellenőrzőösszege" msgid "Update Description" msgstr "Frissítés leírása" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Frissítés hossza" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Frissítés helye" @@ -701,9 +917,6 @@ msgstr "Frissítés összegzése" msgid "Update Version" msgstr "Frissítés verziója" -msgid "Update device firmware on Linux" -msgstr "Eszköz firmware frissítése Linuxon" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "A feltöltési hiba ismert probléma, további információkért látogassa meg ezt az URL-t:" @@ -730,6 +943,11 @@ msgstr "Minden firmware-t az elérhető legfrissebb verziókra frissít" msgid "Updating %s from %s to %s... " msgstr "%s frissítése: %s -> %s…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s frissítése…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Feltöltési üzenet:" @@ -738,6 +956,10 @@ msgstr "Feltöltési üzenet:" msgid "Upload report now?" msgstr "Feltölti most a jelentést?" +#. 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 "A firmware jelentések segítenek a hardvergyártóknak, hogy gyorsan azonosítsák a hibás és sikeres frissítéseket valós eszközökön." + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Felhasználónév" @@ -758,6 +980,10 @@ msgstr "Várakozás…" msgid "Watch DFU devices being hotplugged" msgstr "DFU-eszközök menet közbeni csatlakoztatásának figyelése" +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Hardverváltozások figyelése" + #. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "Firmware írása fájlból egy eszközre" @@ -770,5 +996,6 @@ msgstr "Firmware írása fájlból egy partícióra" msgid "Writing…" msgstr "Írás…" -msgid "fwupd" -msgstr "fwupd" +#. 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 "A disztribúció szállítója nem biztos, hogy ellenőrízte a firmware frissítés kompatibilitását a rendszerével és a kapcsolódó eszközeivel." diff --git a/po/id.po b/po/id.po index 0a99caf0c..93058cb8e 100644 --- a/po/id.po +++ b/po/id.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Indonesian (http://www.transifex.com/freedesktop/fwupd/language/id/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -43,7 +41,7 @@ msgstr "Izinkan penuruntingkatan versi firmware" msgid "Allow re-installing existing firmware versions" msgstr "Izinkan pemasangan ulang versi firmware yang telah ada" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Suatu pembaruan memerlukan boot ulang agar lengkap." @@ -627,12 +625,6 @@ msgstr "Ringkasan" msgid "Target" msgstr "Target" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Proses fwupd adalah sebuah daemon sederhana yang memungkinkan perangkat lunak sesi memperbarui firmware peranti pada mesin lokal Anda. Ini dirancang untuk desktop, tapi proyek ini juga dapat dipakai pada telepon, tablet, dan server headless." - -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Proyek ini bertujuan membuat pemutakhiran firmware pada Linux otomatis, aman, dan handal. Anda dapat memakai manajer perangkat lunak GUI seperti GNOME Perangkat Lunak untuk melihat dan menerapkan pembaruan, perkakas perintah baris, atau antar muka D-Bus secara langsung." - #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" msgstr "Judul" @@ -688,9 +680,6 @@ msgstr "Ringkasan Pembaruan" msgid "Update Version" msgstr "Mutakhirkan Versi" -msgid "Update device firmware on Linux" -msgstr "Perbarui firmware peranti pada Linux" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Kegagalan pembaruan adalah masalah yang telah diketahui, kunjungi URL ini untuk informasi lebih lanjut:" @@ -756,6 +745,3 @@ msgstr "Tulis firmware dari berkas ke dalam suatu partisi" #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Menulis..." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/it.po b/po/it.po index 6d445afaf..f7e407f3a 100644 --- a/po/it.po +++ b/po/it.po @@ -4,13 +4,11 @@ # # Translators: # Gianvito Cavasoli , 2016 -# Milo Casagrande , 2017-2018 +# Milo Casagrande , 2017-2019 msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Italian (http://www.transifex.com/freedesktop/fwupd/language/it/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -30,6 +28,34 @@ msgstr[1] "Mancano %.0f minuti" msgid "%s has firmware updates:" msgstr "%s ha degli aggiornamenti del firmware:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u giorno" +msgstr[1] "%u giorni" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u ora" +msgstr[1] "%u ore" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuto" +msgstr[1] "%u minuti" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u secondo" +msgstr[1] "%u secondi" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Aggiunto" @@ -55,10 +81,14 @@ msgstr "Consente di tornare alle precedenti versioni del firmware" msgid "Allow re-installing existing firmware versions" msgstr "Consente di reinstallare versioni del firmware esistenti" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Per essere completato, un aggiornamento richiede un riavvio." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Per essere completato, un aggiornamento richiede lo spegnimento del sistema." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Risponde affermativamente a tutte le domande" @@ -266,6 +296,11 @@ msgstr "Ripristina una vecchia versione del firmare su un dispositivo" msgid "Downgrading %s from %s to %s... " msgstr "Arretramento di %s da %s a %s..." +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Arretramento di %s…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Scaricamento…" @@ -397,6 +432,9 @@ msgstr "Gli aggiornamenti firmware non sono supportati su questo dispositivo." msgid "Firmware updates are supported on this machine." msgstr "Gli aggiornamenti firmware sono supportati su questo dispositivo." +msgid "Force the action ignoring all warnings" +msgstr "Forza l'azione ignorando gli avvisi" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Trovato" @@ -475,12 +513,6 @@ msgstr "Installa firmware non firmato del dispositivo" msgid "Install unsigned system firmware" msgstr "Installa firmware non firmato di sistema" -#. show message in progressbar -#. TRANSLATORS: %1 is a device name -#, c-format -msgid "Installing %s" -msgstr "Installazione di %s" - #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Installazione aggiornamento firmware…" @@ -597,6 +629,12 @@ msgstr "Permesso negato" msgid "Please enter a number from 0 to %u: " msgstr "Inserire un numero tra 0 e %u:" +msgid "Print the version number" +msgstr "Stampa il numero di versione" + +msgid "Print verbose debug statements" +msgstr "Stampa messaggi di debug prolissi" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Priorità" @@ -743,6 +781,10 @@ msgstr "Condivide la cronologia del firmware con gli sviluppatori" msgid "Show client and daemon versions" msgstr "Mostra la versione del client e del demone" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "Mostra informazioni prolisse del demone" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Mostra le informazioni di debug per tutti i file" @@ -775,6 +817,16 @@ msgstr "Mostra debug dell'ultimo tentativo di aggiornamento" msgid "Show the information of firmware update status" msgstr "Mostra informazioni sullo stato degli aggiornamenti firmware" +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Spegnere ora?" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Specifica vendor/ID prodotto di un dispositivo DFU" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Specifica il numero di byte per trasferimento USB" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Stato" @@ -796,20 +848,10 @@ msgstr "Obiettivo" 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 è un servizio gratuito che opera come entità legale indipendente e non ha alcun legame con $OS_RELEASE:NAME$. Il distributore potrebbe non aver verificato la compatibilità degli aggiornamenti firmware col proprio sistema o con i propri dispositivi collegati. Il firmware viene fornito solamente dall'OEM." -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Il processo fwupd è un demone che consente di aggiornare il firmware di un dispositivo sul proprio computer. È progettato per un ambiente desktop, ma è possibile utilizzarlo anche su telefonini, tablet e server." - -#. TRANSLATORS: exactly one update needs this -msgid "The update requires a reboot to complete." -msgstr "Per essere completato, l'aggiornamento richiede un riavvio." - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Questo programma può funzionare correttamente solo come utente root" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Questo progetto vuole rendere l'aggiornamento di firmware su Linux un processo automatico, sicuro e affidabile. È possibile usare uno strumento grafico come GNOME Software per visualizzare e applicare gli aggiornamenti, oppure lo strumento a riga di comando o l'interfaccia D-Bus." - 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 "Questo remoto contiene firmware che non è bloccato, ma è ancora in fase di verifica dal produttore hardware. Assicurarsi di poter ripristinare, manualmente o con altre procedure, il vecchio firmware nel caso in cui l'aggiornamento non riuscisse." @@ -856,6 +898,11 @@ msgstr "Codice di controllo aggiornamento" msgid "Update Description" msgstr "Descrizione aggiornamento" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Durata aggiornamento" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Posizione aggiornamento" @@ -876,8 +923,9 @@ msgstr "Riepilogo aggiornameto" msgid "Update Version" msgstr "Versione aggiornamento" -msgid "Update device firmware on Linux" -msgstr "Aggiorna firmware dispositivi su Linux" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Aggiorna tutti i dispositivi corrispondenti ai metadati locali" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -905,6 +953,11 @@ msgstr "Aggiorna tutti i firmware all'ultima versione disponibile" msgid "Updating %s from %s to %s... " msgstr "Aggiornamento di %s da %s a %s..." +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Aggiornamento di %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Messaggio di caricamento:" @@ -956,6 +1009,3 @@ msgstr "Scrittura…" #. 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 "La propria distribuzione potrebbe non aver verificato la compatibilità degli aggiornamenti firmware col proprio sistema o col dispositivo collegato." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/kk.po b/po/kk.po index 6fa7e2e9c..799aa994b 100644 --- a/po/kk.po +++ b/po/kk.po @@ -8,9 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-01 14:26+0100\n" -"PO-Revision-Date: 2017-09-01 13:27+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Kazakh (http://www.transifex.com/freedesktop/fwupd/language/kk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/ko.po b/po/ko.po index 45d7182cf..6fbb5e481 100644 --- a/po/ko.po +++ b/po/ko.po @@ -9,8 +9,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Korean (http://www.transifex.com/freedesktop/fwupd/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -48,7 +46,7 @@ msgstr "펌웨어 다운그레이드를 허용합니다" msgid "Allow re-installing existing firmware versions" msgstr "기존 펌웨어 버전 재설치를 허용합니다" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "업데이트를 완료하려면 다시 시작해야 합니다." @@ -769,16 +767,10 @@ msgstr "대상" 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$와(과) 별개로 운영되는 독립된 법적 단체에서 운영하는 무료 서비스입니다. 배포판 개발사에서 펌웨어 업데이트와 시스템 및 연결된 장치간의 호환성을 검증한다는 보장이 없습니다. 모든 펌웨어는 원 장치 제조사(OEM)에서 직접 제공합니다." -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "fwupd 프로세스는 로컬 머신의 장치 펌웨어를 세션 프로그램에서 업데이트할 수 있게 하는 간단한 데몬입니다. 데스크톱용으로 설계했지만, 전화기, 태블릿, 헤드리스 서버에서도 사용할 수 있습니다." - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "이 프로그램은 루트 권한으로만 올바르게 작동할 수도 있습니다" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "이 프로젝트에서는 리눅스에서 자동으로 안전하고 믿을 수 있게 펌웨어를 업데이트할 수 있게 하려고 합니다. 그놈 소프트웨어와 같은 GUI 도구로 업데이트를 보고 적용하거나, 명령행 도구 및 D-Bus 인터페이스를 직접 사용할 수 있습니다." - 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 "이 원격 저장소에서는 하드웨어 제조사에서 검증 단계를 진행 중인 펌웨어를 배포합니다. 펌웨어 업데이트 도중 및 이후 문제가 발생했을 때 수동으로 펌웨어를 다운그레이드할 방법을 찾아 두는 것을 추천합니다." @@ -845,9 +837,6 @@ msgstr "업데이트 요약" msgid "Update Version" msgstr "업데이트 버전" -msgid "Update device firmware on Linux" -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을 참조가힙시오:" @@ -925,6 +914,3 @@ 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 "배포판 개발사에서 펌웨어 업데이트와 시스템 및 연결된 장치간의 호환성을 검증한다는 보장이 없습니다." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/ky.po b/po/ky.po index 11c96b915..f8ed34ec2 100644 --- a/po/ky.po +++ b/po/ky.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Kyrgyz (http://www.transifex.com/freedesktop/fwupd/language/ky/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/nl.po b/po/nl.po index 1a89627d2..c8b395ce8 100644 --- a/po/nl.po +++ b/po/nl.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Dutch (http://www.transifex.com/freedesktop/fwupd/language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/oc.po b/po/oc.po index c59e18255..21d8bd97b 100644 --- a/po/oc.po +++ b/po/oc.po @@ -9,8 +9,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Occitan (post 1500) (http://www.transifex.com/freedesktop/fwupd/language/oc/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/pl.po b/po/pl.po index 5d24f784a..e53649f2c 100644 --- a/po/pl.po +++ b/po/pl.po @@ -3,13 +3,11 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Piotr Drąg , 2015-2018 +# Piotr Drąg , 2015-2019 msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Polish (http://www.transifex.com/freedesktop/fwupd/language/pl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -31,6 +29,42 @@ msgstr[3] "Pozostało %.0f minut" msgid "%s has firmware updates:" msgstr "Dostępne są aktualizacje oprogramowania sprzętowego dla urządzenia %s:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dzień" +msgstr[1] "%u dni" +msgstr[2] "%u dni" +msgstr[3] "%u dni" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u godzina" +msgstr[1] "%u godziny" +msgstr[2] "%u godzin" +msgstr[3] "%u godzin" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuta" +msgstr[1] "%u minuty" +msgstr[2] "%u minut" +msgstr[3] "%u minut" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekunda" +msgstr[1] "%u sekundy" +msgstr[2] "%u sekund" +msgstr[3] "%u sekund" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Dodano" @@ -56,10 +90,14 @@ msgstr "Umożliwia instalowanie poprzednich wersji oprogramowania sprzętowego" msgid "Allow re-installing existing firmware versions" msgstr "Umożliwia ponowne instalowanie istniejących wersji oprogramowania sprzętowego" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Ukończenie aktualizacji wymaga ponownego uruchomienia." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Ukończenie aktualizacji wymaga wyłączenia komputera." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Odpowiada tak na wszystkie pytania" @@ -267,6 +305,11 @@ msgstr "Instaluje poprzednią wersję oprogramowania sprzętowego urządzenia" msgid "Downgrading %s from %s to %s... " msgstr "Instalowanie poprzedniej wersji %s z %s do %s… " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Instalowanie poprzedniej wersji urządzenia %s…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Pobieranie…" @@ -400,6 +443,9 @@ msgstr "Aktualizacje oprogramowania sprzętowego nie są obsługiwane na tym kom msgid "Firmware updates are supported on this machine." msgstr "Aktualizacje oprogramowania sprzętowego są obsługiwane na tym komputerze." +msgid "Force the action ignoring all warnings" +msgstr "Wymusza działanie ignorując wszystkie ostrzeżenia" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Odnaleziono" @@ -478,12 +524,6 @@ msgstr "Instalacja niepodpisanego oprogramowania sprzętowego urządzenia" msgid "Install unsigned system firmware" msgstr "Instalacja niepodpisanego oprogramowania sprzętowego komputera" -#. show message in progressbar -#. TRANSLATORS: %1 is a device name -#, c-format -msgid "Installing %s" -msgstr "Instalowanie urządzenia %s" - #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Instalowanie aktualizacji oprogramowania sprzętowego…" @@ -600,6 +640,12 @@ msgstr "Brak uprawnień" msgid "Please enter a number from 0 to %u: " msgstr "Proszę podać liczbę od 0 do %u:" +msgid "Print the version number" +msgstr "Wyświetla numer wersji" + +msgid "Print verbose debug statements" +msgstr "Wyświetla więcej komunikatów debugowania" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Priorytet" @@ -746,6 +792,10 @@ msgstr "Udostępnia historię oprogramowania sprzętowego programistom" msgid "Show client and daemon versions" msgstr "Wyświetla wersje klienta i usługi" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "Wyświetla więcej informacji o usłudze" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Wyświetla informacje o debugowaniu dla wszystkich plików" @@ -778,6 +828,16 @@ msgstr "Wyświetla dziennik debugowania z ostatniej aktualizacji" msgid "Show the information of firmware update status" msgstr "Wyświetla informacje o stanie aktualizacji oprogramowania sprzętowego" +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Wyłączyć teraz?" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Podaje identyfikatory dostawcy/produktu urządzenia DFU" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Podaje liczbę bajtów na przesyłanie USB" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Stan" @@ -799,20 +859,10 @@ msgstr "Cel" 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 to wolny serwis działający jako niezależny podmiot prawny niemający związków z systemem $OS_RELEASE:NAME$. Dystrybutor używanego systemu mógł nie zweryfikować żadnych aktualizacji oprogramowania sprzętowego pod kątem zgodności z używanym komputerem lub podłączonymi urządzeniami. Każde oprogramowanie sprzętowe jest dostarczane wyłącznie przez oryginalnego producenta sprzętu." -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Proces fwupd to prosta usługa umożliwiająca aktualizowanie oprogramowania sprzętowego komputera w sesji użytkownika. Jest zaprojektowana dla komputerów osobistych, ale można jej używać także na telefonach, tabletach i serwerach bez monitorów." - -#. TRANSLATORS: exactly one update needs this -msgid "The update requires a reboot to complete." -msgstr "Ukończenie aktualizacji wymaga ponownego uruchomienia." - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Ten program może działać poprawnie tylko jako root" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Celem projektu jest automatyczne, bezpieczne i pewne aktualizowanie oprogramowania sprzętowego w systemie Linux. Można używać graficznego menedżera oprogramowania, takiego jak Menedżer oprogramowania GNOME do wyświetlania i zastosowywania aktualizacji, narzędzia wiersza poleceń lub bezpośrednio interfejsu D-Bus." - 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 "To repozytorium zawiera oprogramowanie sprzętowe nieobjęte embargo, ale nadal testowane przez dostawcę sprzętu. Należy upewnić się, że istnieje możliwość ręcznego zainstalowania poprzedniej wersji oprogramowania sprzętowego w razie niepowodzenia aktualizacji." @@ -859,6 +909,11 @@ msgstr "Suma kontrolna aktualizacji" msgid "Update Description" msgstr "Opis aktualizacji" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Czas trwania aktualizacji" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Położenie aktualizacji" @@ -879,8 +934,9 @@ msgstr "Podsumowanie aktualizacji" msgid "Update Version" msgstr "Wersja aktualizacji" -msgid "Update device firmware on Linux" -msgstr "Aktualizowanie oprogramowania sprzętowego w systemie Linux" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Aktualizuje wszystkie urządzenia pasujące do lokalnych metadanych" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -908,6 +964,11 @@ msgstr "Aktualizuje całe oprogramowanie sprzętowe do najnowszych dostępnych w msgid "Updating %s from %s to %s... " msgstr "Aktualizowanie %s z wersji %s do %s… " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Aktualizowanie urządzenia %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Komunikat wysyłania:" @@ -959,6 +1020,3 @@ msgstr "Zapisywanie…" #. 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 "Używana dystrybucja mogła nie sprawdzić zgodności aktualizacji oprogramowania sprzętowego z komputerem i podłączonymi urządzeniami." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/pt_BR.po b/po/pt_BR.po index de533624d..7339bdc18 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -6,14 +6,12 @@ # Derek W. Stavis , 2015 # Derek W. Stavis , 2016 # Derek W. Stavis , 2015-2016 -# Rafael Fontenelle , 2017 +# Rafael Fontenelle , 2017-2018 # Rafael Fontenelle , 2015-2018 msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/freedesktop/fwupd/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,11 +19,46 @@ msgstr "" "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +#. 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" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s tem atualizações de firmware:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dia" +msgstr[1] "%u dias" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hora" +msgstr[1] "%u horas" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuto" +msgstr[1] "%u minutos" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u segundo" +msgstr[1] "%u segundos" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Adicionado" @@ -45,13 +78,13 @@ msgstr "Atalho para %s" #. TRANSLATORS: command line option msgid "Allow downgrading firmware versions" -msgstr "Permite reverter versões de firmware" +msgstr "Permite fazer downgrade de versões de firmware" #. TRANSLATORS: command line option msgid "Allow re-installing existing firmware versions" msgstr "Permite reinstalar versões existentes de firmware" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Uma atualização requer uma reinicialização para completar." @@ -86,11 +119,11 @@ msgstr "Autenticando…" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "É requerida autenticação para voltar a versão do firmware em um dispositivo removível" +msgstr "É requerida autenticação para fazer downgrade da versão do firmware em um dispositivo removível" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" -msgstr "É requerida autenticação para voltar a versão do firmware nesta máquina" +msgstr "É requerida autenticação para fazer downgrade da versão do firmware nesta máquina" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" @@ -253,14 +286,19 @@ msgstr "Feito!" #. TRANSLATORS: command description msgid "Downgrades the firmware on a device" -msgstr "Retrocede a versão do firmware em um dispositivo" +msgstr "Faz downgrade da versão do firmware em um dispositivo" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" #, c-format msgid "Downgrading %s from %s to %s... " -msgstr "Revertendo %s de %s para %s… " +msgstr "Fazendo downgrade de %s de %s para %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Fazendo downgrade %s…" #. TRANSLATORS: downloading from a remote server msgid "Downloading…" @@ -393,6 +431,9 @@ msgstr "Não há suporte a atualizações de firmware nessa máquina." msgid "Firmware updates are supported on this machine." msgstr "Há suporte a atualizações de firmware nesta máquina." +msgid "Force the action ignoring all warnings" +msgstr "Força a ação ignorando todos avisos" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Encontrado" @@ -471,19 +512,22 @@ msgstr "Instalar firmware não assinado no dispositivo" msgid "Install unsigned system firmware" msgstr "Instalar firmware não assinado no sistema" -#. show message in progressbar -#. TRANSLATORS: %1 is a device name -#, c-format -msgid "Installing %s" -msgstr "Instalando %s" - #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Instalando atualização de firmware…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instalando em %s…" + msgid "Keyring" msgstr "Chaveiro" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Menos que um minuto restante" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (firmware estável)" @@ -584,6 +628,12 @@ msgstr "Permissão negada" msgid "Please enter a number from 0 to %u: " msgstr "Por favor, insira um número de 0 a %u: " +msgid "Print the version number" +msgstr "Imprime o número de versão" + +msgid "Print verbose debug statements" +msgstr "Imprime instruções de depuração verbosas" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Prioridade" @@ -730,6 +780,10 @@ msgstr "Compartilha histórico de firmware com os desenvolvedores" msgid "Show client and daemon versions" msgstr "Mostra as versões do cliente e do daemon" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "Mostra informações verbosas de daemon" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Mostrar informações de depuração para todos os arquivos" @@ -762,6 +816,12 @@ msgstr "Mostra o registro log de depuração da tentativa mais recente de atuali msgid "Show the information of firmware update status" msgstr "Mostra as informações do status de atualização de firmware" +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifica ID(s) de Fornecedor/Produto de dispositivo DFU" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifica o número de bytes por transferência USB" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Estado" @@ -783,16 +843,10 @@ msgstr "Alvo" 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 "O LVFS é um serviço livre que opera como uma entidade legal independente e tem nenhuma conexão com $OS_RELEASE:NAME$. Seu distribuidor pode não ter verificado alguma das atualizações de firmware por compatibilidade com seu sistema ou dispositivos conectados. Todos os firmwares são fornecidos apenas pelo fabricante do equipamento original." -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "O fwupd processa é um daemon simples para permitir que software sessão atualizem firmware de dispositivo em seu computador local. É projetado para computadores de mesa, mas esse projeto também é usável em telefones, tablets e em servidores “headless” (sem monitor, teclado e mouse)." - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Esse programa só pode funcionar corretamente como root" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Esse projeto visa tornar a atualização de firmware no Linux automática, segura e confiável. você pode usar um gerenciador de software GUI, como o GNOME Software, para ver e aplicar atualizações, a ferramenta de linha de comando ou a interface D-Bus diretamente." - 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 "Este remoto contém firmware que não está embargado, mas ainda está sendo testado pelo fornecedor do hardware. Você deve garantir que você tenha uma maneira de fazer downgrade manual do firmware se a atualização do firmware falhar." @@ -839,6 +893,11 @@ msgstr "Soma de verificação da atualização" msgid "Update Description" msgstr "Descrição da atualização" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Duração da atualização" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Local da atualização" @@ -859,9 +918,6 @@ msgstr "Resumo da atualização" msgid "Update Version" msgstr "Versão da atualização" -msgid "Update device firmware on Linux" -msgstr "Atualize firmware de dispositivos no Linux" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "A falha de atualização é um problema conhecido, visite essa URL para mais informações:" @@ -888,6 +944,11 @@ msgstr "Atualiza todos os firmwares para a última versão disponível" msgid "Updating %s from %s to %s... " msgstr "Atualizando %s de %s para %s… " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Atualizando %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Mensagem enviada:" @@ -939,6 +1000,3 @@ msgstr "Escrevendo…" #. 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 "Seu distribuidor pode não ter verificado qualquer das atualizações de firmware para compatibilidade com seu sistema ou dispositivos conectados." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/ru.po b/po/ru.po index 46963e16f..631c93f8e 100644 --- a/po/ru.po +++ b/po/ru.po @@ -9,8 +9,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Russian (http://www.transifex.com/freedesktop/fwupd/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -547,12 +545,6 @@ msgstr "Состояние" msgid "Status" msgstr "Статус" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Процесс fwupd является простой фоновой службой, позволяющей сеансовому программному обеспечению обновлять микропрограммы устройств на вашем компьютере. Он разработан для настольных компьютеров, но этот проект также годен для использования на телефонах, планшетах и безмониторных серверах." - -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Целью этого проекта является безопасная и надёжная автоматизация обновления микропрограмм в Linux. Для просмотра и применения обновлений вы можете использовать как графический диспетчер программного обеспечения, такой как GNOME Software, так и инструментарий командной строки или даже непосредственно D-Bus интерфейс." - #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" msgstr "Заголовок" @@ -600,9 +592,6 @@ msgstr "Удалённый ID обновления" msgid "Update Version" msgstr "Версия обновления" -msgid "Update device firmware on Linux" -msgstr "Обновить микропрограмму устройства на Linux" - msgid "Update the stored device verification information" msgstr "Обновление хранимой проверочной информации устройства" @@ -652,6 +641,3 @@ msgstr "Записать микропрограмму из файла на од #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Запись…" - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/sk.po b/po/sk.po index 4329736b4..fa403fdaf 100644 --- a/po/sk.po +++ b/po/sk.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Slovak (http://www.transifex.com/freedesktop/fwupd/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/sr.po b/po/sr.po index e67ef259a..8c5d1e6bb 100644 --- a/po/sr.po +++ b/po/sr.po @@ -10,8 +10,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Serbian (http://www.transifex.com/freedesktop/fwupd/language/sr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -45,7 +43,7 @@ msgstr "Дозволи уназађивање издања фирмвера" msgid "Allow re-installing existing firmware versions" msgstr "Дозволи поновно инсталирање већ постојећих издања фирмвера" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Потребно је поново покретање да би се исправка применила." @@ -631,12 +629,6 @@ msgstr "Сажетак" msgid "Target" msgstr "Мета" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Fwupd процес је једноставан демно који омогућава програмиму у сесији да ажурира фирмвер уређаја на вашој локалној машини. Намењен је за стоне рачунаре али је овај пројекат могуће користити на телефонима, таблетима и безглавим серверима." - -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Сврха овог пројекта је да учини ажурирање фирмвера на Линуксу лаким, безбедним и поузданим. Можете користити графичког управника програма као што су то Гномови Програми да бисте гледали и примењивали исправке, командну алатку или D-Bus интерфејс директно." - #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" msgstr "Наслов" @@ -692,9 +684,6 @@ msgstr "Сажетак ажурирања" msgid "Update Version" msgstr "Верзија ажурирања" -msgid "Update device firmware on Linux" -msgstr "Ажурирајте фирмвер уређаја на Линуксу" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Узрок неуспеха ажурирања је познат, погледајте ову адресу за више података:" @@ -760,6 +749,3 @@ msgstr "Упиши фирмвер из датотеке у једну парти #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Пишем…" - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/sv.po b/po/sv.po index 128f5de4e..de6d84139 100644 --- a/po/sv.po +++ b/po/sv.po @@ -3,17 +3,15 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Anders Jonsson , 2017 +# Anders Jonsson , 2017,2019 # Andreas Henriksson , 2017 # Josef Andersson , 2015,2017-2018 # Josef Andersson , 2015,2017 -# sebras , 2018 +# Sebastian Rasmussen , 2018 msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Swedish (http://www.transifex.com/freedesktop/fwupd/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,11 +19,46 @@ msgstr "" "Language: sv\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0fminut kvarstår" +msgstr[1] "%.0f minuter kvarstår" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s har uppdateringar för fast programvara:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dag" +msgstr[1] "%u dagar" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u timme" +msgstr[1] "%u timmar" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%uminut" +msgstr[1] "%u minuter" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekund" +msgstr[1] "%u sekunder" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Tillagd" @@ -51,7 +84,7 @@ msgstr "Tillåt att nedgradera versioner av fast programvara" msgid "Allow re-installing existing firmware versions" msgstr "Tillåt att installera om befintliga versioner av fast programvara" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "En uppdatering kräver en omstart för att färdigställas." @@ -63,10 +96,18 @@ msgstr "Svara ja på alla frågor" msgid "Apply a binary patch" msgstr "Applicera en binär lagning" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Tillämpa uppdateringar för fast programvara" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Anslut DFU-kapabel enhet till körtid" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Fäst till fast programvaruläge" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -189,6 +230,10 @@ msgstr "Beskrivning" msgid "Detach currently attached DFU capable device" msgstr "Koppla från anslutna DFU-kapabla enheter" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Koppla från till starthanterarläge" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Enhet tillagd:" @@ -209,10 +254,17 @@ msgstr "Enheter som har uppdaterats:" msgid "Devices that were not updated correctly:" msgstr "Enheter som inte uppdaterades korrekt:" +msgid "Disabled fwupdate debugging" +msgstr "Inaktiverade fwupdate-felsökning" + #. TRANSLATORS: command description msgid "Disables a given remote" msgstr "Inaktiverar en given fjärr." +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Visa version" + #. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Kontrollera inte gammal metadata" @@ -225,6 +277,10 @@ msgstr "Kontrollera inte om det krävs omstart efter uppdatering" msgid "Do not check for unreported history" msgstr "Kontrollera inte ej rapporterad historik" +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Skriv inte till historikdatabasen" + msgid "Done!" msgstr "Klar!" @@ -239,6 +295,11 @@ msgstr "Nergradera fast programvara på en enhet" msgid "Downgrading %s from %s to %s... " msgstr "Nedgraderar %s från %s till %s… " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Nedgraderar %s…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Laddar ner..." @@ -255,6 +316,14 @@ msgstr "Skriv ut detaljer om fast programvarufil" msgid "Dump information about a binary patch to the screen" msgstr "Dumpa informationen om en binär lagning till skärmen" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Angiven ESP var inte giltig" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Aktivera stöd för uppdatering av fast programvara på system som stöds" + #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" msgstr "Aktivera denna fjärr?" @@ -263,10 +332,16 @@ msgstr "Aktivera denna fjärr?" msgid "Enabled" msgstr "Aktiverad" +msgid "Enabled fwupdate debugging" +msgstr "Aktiverade fwupdate-felsökning" + #. TRANSLATORS: command description msgid "Enables a given remote" msgstr "Aktiverar en given fjärr." +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 "Att aktivera denna funktionalitet görs på egen risk, vilket betyder att du måste kontakta den ursprungliga tillverkaren av din utrustning om problem som orsakas av dessa uppdateringar. Endast problem med själva uppdateringsprocessen ska rapporteras på $OS_RELEASE:BUG_REPORT_URL$." + #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." msgstr "Att aktivera denna fjärr görs på egen risk." @@ -291,6 +366,10 @@ msgstr "Avsluta efter en kort fördröjning" msgid "Exit after the engine has loaded" msgstr "Avsluta efter att motorn har lästs in" +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Misslyckades med hämtning på grund av servergräns" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Misslyckades med att läsa in speciallösning" @@ -346,6 +425,15 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "Metadata för fast programvara har inte uppdaterats på %udag och kan vara utdaterad." msgstr[1] "Metadata för fast programvara har inte uppdaterats på %udagar och kan vara utdaterad." +msgid "Firmware updates are not supported on this machine." +msgstr "Uppdateringar av fast programvara stöds inte på denna maskin." + +msgid "Firmware updates are supported on this machine." +msgstr "Uppdateringar av fast programvara stöds på denna maskin." + +msgid "Force the action ignoring all warnings" +msgstr "Tvinga åtgärden, ignorera alla varningar" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Hittad" @@ -353,10 +441,18 @@ msgstr "Hittad" msgid "GUID" msgstr "GUID" +#. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Hämta alla enheter enligt systemets topologi" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Hämta alla enheter som stödjer uppdateringar av fast programvara" +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Erhåll alla aktiverade insticksmoduler som är registrerade i systemet" + #. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "Hämtar detaljer om en fast programvarufil" @@ -389,6 +485,10 @@ msgstr "ID" msgid "Idle…" msgstr "Väntar…" +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Installera en fast programvaru-blob på en enhet" + #. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Installera en fast programvarufil på denna hårdvara" @@ -416,9 +516,18 @@ msgstr "Installera osignerad fast programvara för systemet" msgid "Installing firmware update…" msgstr "Installerar uppdatering för fast programvara…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installerar på %s…" + msgid "Keyring" msgstr "Nyckelring" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Mindre än en minut kvarstår" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (stabil fastprogramvara)" @@ -429,10 +538,18 @@ msgstr "Linux Vendor Firmware Service (fastprogramvara för testning)" msgid "List currently attached DFU capable devices" msgstr "Lista aktuella anslutna DFU-kapabla enheter" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Lista uppdateringar för fast programvara som stöds" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Läser in…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Vitlista manuellt specifika insticksmoduler" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "Sammanfoga flera fasta programvaror till en" @@ -469,11 +586,18 @@ msgstr "Övervaka demonen för händelser" msgid "Name" msgstr "Namn" +msgid "No action specified!" +msgstr "Ingen åtgärd angiven!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Ingen uppdateringsbar hårdvara upptäcktes" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Inga insticksmoduler hittades" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Inga fjärrar är aktiverade för närvarande, så ingen metadata är tillgänglig." @@ -483,7 +607,11 @@ msgstr "OK" #. TRANSLATORS: command line option msgid "Override plugin warning" -msgstr "Åsidosätt tilläggsvarning" +msgstr "Åsidosätt insticksmodulvarning" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Åsidosätt standardsökväg för ESP" #. TRANSLATORS: remote filename base msgid "Password" @@ -500,6 +628,12 @@ msgstr "Åtkomst nekad" msgid "Please enter a number from 0 to %u: " msgstr "Ange en siffra mellan 0 och %u: " +msgid "Print the version number" +msgstr "Skriv ut versionsnumret" + +msgid "Print verbose debug statements" +msgstr "Skriv ut utförliga felsökningssatser" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Prioritet" @@ -511,6 +645,10 @@ msgstr "Fortsätt med att skicka upp?" msgid "Protocol" msgstr "Protokoll" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Fråga efter stöd för uppdatering av fast programvara" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -618,6 +756,10 @@ msgstr "Ange produkt-ID för den fasta programvarufilen" msgid "Set release version on firmware file" msgstr "Ange utgåveversion för den fasta programvarufilen" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Ställ in felsökningsflaggan under uppdatering" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Ange fast programvarustorlek för målet" @@ -638,6 +780,10 @@ msgstr "Dela fast programvaruhistorik med utvecklarna" msgid "Show client and daemon versions" msgstr "Visa klient- och demon-version" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "Visa utförlig information om demon" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Visa felsökningsinformation för alla filer" @@ -646,18 +792,36 @@ msgstr "Visa felsökningsinformation för alla filer" msgid "Show debugging options" msgstr "Visa felsökningsalternativ" +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Visa enheter som inte kan uppdateras" + #. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Visa extra felsökningsinformation" #. TRANSLATORS: command description msgid "Show history of firmware updates" -msgstr "Visa historik över fasta programvaruupdateringar" +msgstr "Visa historik över fasta programvaruuppdateringar" #. TRANSLATORS: this is for plugin development msgid "Show plugin verbose information" msgstr "Visa utförlig information om insticksmodul" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Visa felsökningsloggen från det senaste uppdateringsförsöket" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Visa information om uppdateringsstatus för fast programvara" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Ange Tillverkar-/Produkt-ID för DFU-enhet" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Ange antalet byte per USB-överföring" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Tillstånd" @@ -675,11 +839,16 @@ msgstr "Sammanfattning" msgid "Target" msgstr "Mål" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Fwupd-processen är en enkel demon som tillåter sessions-programvara att uppdatera enheters fasta programvara på din lokala maskin. Det är utformat för skrivbordsmiljöer, men detta projekt är också användbart på telefoner, surfplattor och skärmlösa servrar." +#. 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 är en fri tjänst som fungerar som en oberoende juridisk person och har ingen koppling till $OS_RELEASE:NAME$. Din distributör kanske inte har bekräftat att någon av de fasta programvaruuppdateringarna är kompatibla med ditt system eller anslutna enheter. All fast programvara tillhandahålls endast av den ursprungliga tillverkaren av utrustning." -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Detta projekt strävar efter att göra uppdatering av fast programvara på Linux automatisk, säker och pålitlig. Du kan antingen använda ett grafiskt användargränssnitt som GNOME Programvara för att granska och applicera uppdateringar, ett kommandoradsverktyg eller direkt mot D-Bus gränssnittet." +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Detta program kommer endast fungera korrekt som root" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "Denna fjärrkälla innehåller fast programvara som inte är under embargo, men fortfarande testas av hårdvarutillverkare. Du bör säkerställa att du har ett sätt att manuellt nedgradera den fasta programvaran om uppdateringen misslyckas." #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -693,6 +862,10 @@ msgstr "Överföringsstorlek" msgid "Type" msgstr "Typ" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Verktyg för fast UEFI-programvara" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -708,6 +881,10 @@ msgstr "Lås upp enheten för att tillåta åtkomst" msgid "Unlocks the device for firmware access" msgstr "Låser upp enheten för fast programvaruåtkomst" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Ta bort felsökningsflaggan under uppdatering" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Uppdateringskontrollsumma" @@ -716,6 +893,11 @@ msgstr "Uppdateringskontrollsumma" msgid "Update Description" msgstr "Uppdateringsbeskrivning" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Uppdateringslängd" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Uppdateringsplats" @@ -736,9 +918,6 @@ msgstr "Uppdateringssammanfattning" msgid "Update Version" msgstr "Uppdateringsversion" -msgid "Update device firmware on Linux" -msgstr "Uppdatera enhetens fasta programvara på Linux" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Misslyckad uppdatering är ett känt fel, besök denna url för mer information:" @@ -765,6 +944,11 @@ msgstr "Uppdaterar all fast programvara till de senast tillgängliga versionerna msgid "Updating %s from %s to %s... " msgstr "Uppdaterar %s från %s till %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Uppdaterar %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Uppladdningsmeddelande:" @@ -797,6 +981,10 @@ msgstr "Väntar..." msgid "Watch DFU devices being hotplugged" msgstr "Övervaka anslutna DFU-enheter" +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Övervaka hårdvaruändringar" + #. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "Skriv fast programvara från fil till enhet" @@ -812,6 +1000,3 @@ msgstr "Skriver…" #. 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 "Din distributör kanske inte har verifierat någon av uppdateringarna av fastprogramvara för kompatibilitet med ditt system eller anslutna enheter." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/tr.po b/po/tr.po index c18f1cb09..41770aaf0 100644 --- a/po/tr.po +++ b/po/tr.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Turkish (http://www.transifex.com/freedesktop/fwupd/language/tr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/uk.po b/po/uk.po index 4a2732a0d..ad1bd3268 100644 --- a/po/uk.po +++ b/po/uk.po @@ -3,13 +3,11 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Yuri Chornoivan , 2015-2018 +# Yuri Chornoivan , 2015-2019 msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Ukrainian (http://www.transifex.com/freedesktop/fwupd/language/uk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -31,6 +29,42 @@ msgstr[3] "Лишилася %.0f хвилина" msgid "%s has firmware updates:" 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 день" + +#. 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 година" + +#. 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 хвилина" + +#. 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 секунда" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Додано" @@ -56,10 +90,14 @@ msgstr "Дозволити зниження версій мікропрогра msgid "Allow re-installing existing firmware versions" msgstr "Дозволити повторне встановлення наявних версій мікропрограми" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Для завершення оновлення слід перезавантажити систему." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Для завершення оновлення систему слід вимкнути." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Відповідати «так» на усі питання" @@ -267,6 +305,11 @@ msgstr "Знижує версію мікропрограми на пристро msgid "Downgrading %s from %s to %s... " msgstr "Знижуємо версію %s з %s до %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Знижуємо версію %s…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Отримуємо дані…" @@ -400,6 +443,9 @@ msgstr "На цьому комп'ютері не передбачено підт msgid "Firmware updates are supported on this machine." msgstr "На цьому комп'ютері передбачено підтримку оновлень мікропрограми." +msgid "Force the action ignoring all warnings" +msgstr "Виконати дію примусово, ігноруючи усі попередження" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Знайдено" @@ -478,12 +524,6 @@ msgstr "Встановити непідписану мікропрограму msgid "Install unsigned system firmware" msgstr "Встановити непідписану мікропрограму системи" -#. show message in progressbar -#. TRANSLATORS: %1 is a device name -#, c-format -msgid "Installing %s" -msgstr "Встановлюємо %s" - #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Встановлюємо оновлення мікропрограми…" @@ -600,6 +640,12 @@ msgstr "Доступ заборонено" msgid "Please enter a number from 0 to %u: " msgstr "Будь ласка, введіть число від 0 до %u: " +msgid "Print the version number" +msgstr "Вивести номер версії" + +msgid "Print verbose debug statements" +msgstr "Вивести докладні діагностичні повідомлення" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Пріоритетність" @@ -746,6 +792,10 @@ msgstr "Поділитися журналом оновлень із розроб msgid "Show client and daemon versions" msgstr "Вивести дані щодо версій клієнат і фонової служби" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information" +msgstr "Показати докладні відомості щодо фонової служби" + #. TRANSLATORS: turn on all debugging msgid "Show debugging information for all files" msgstr "Показувати діагностичні дані для всіх файлів" @@ -778,6 +828,16 @@ msgstr "Показати діагностичний журнал щодо ост msgid "Show the information of firmware update status" msgstr "Показати дані щодо стану оновлення мікропрограми" +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Вимкнути зараз?" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Вказати ідентифікатори виробника/продукту пристрою DFU" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Вказати кількість байтів на один пакет передавання даних USB" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Стан" @@ -799,20 +859,10 @@ msgstr "Ціль" 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$. Розробники вашої операційної системи, можливо, не перевіряли жодні з цих оновлень на сумісність із системою або з'єднаними із комп'ютером пристроями. Усі мікропрограми надаються лише самими виробниками обладнання." -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "Процес fwupd є простою фоновою службою, яка надає змогу оновлювати мікропрограми пристроїв на вашому локальному комп’ютері у межах сеансу користування. Програму розроблено для робочих станцій, але нею можна скористатися на телефонах, планшетах та серверах без дисплеїв." - -#. TRANSLATORS: exactly one update needs this -msgid "The update requires a reboot to complete." -msgstr "Для завершення оновлення потрібне перезавантаження." - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Програма зможе працювати належними чином лише від імені користувача root" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "Метою цього проекту є автоматизація оновлення мікропрограм обладнання у Linux, безпечно і надійно. Для перегляду і застосування оновлень ви можете скористатися або програмою для керування програмним забезпеченням, зокрема Програмними засобами GNOME, або інструментом командного рядка, або безпосередньо інтерфейсом D-Bus." - 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 "У цьому сховищі міститься мікропрограма, встановлювати яку не заборонено, але яка усе ще перебуває у процесі тестування виробником обладнання. Вам слід переконатися, що ви зможете встановити стабільну версію мікропрограми, якщо процедура оновлення зазнає невдачі." @@ -859,6 +909,11 @@ msgstr "Контрольна сума оновлення" msgid "Update Description" msgstr "Опис оновлення" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Тривалість оновлення" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Місце оновлення" @@ -879,8 +934,9 @@ msgstr "Резюме оновлення" msgid "Update Version" msgstr "Версія оновлення" -msgid "Update device firmware on Linux" -msgstr "Оновлення мікропрограм пристроїв у Linux" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Оновити усі пристрої, які відповідають локальним метаданим" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -908,6 +964,11 @@ msgstr "Оновлює усі мікропрограми до найновіши msgid "Updating %s from %s to %s... " msgstr "Оновлюємо %s з %s до %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Оновлюємо %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Повідомлення про вивантаження:" @@ -959,6 +1020,3 @@ 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 "Виробник вашого дистрибутива може не перевіряти усі оновлення мікропрограми на сумісність із вашою системою або з’єднаними із нею пристроями." - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/zh_CN.po b/po/zh_CN.po index 88d8221fc..32165fdff 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -12,8 +12,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Chinese (China) (http://www.transifex.com/freedesktop/fwupd/language/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -51,7 +49,7 @@ msgstr "允许降级固件版本" msgid "Allow re-installing existing firmware versions" msgstr "允许重新安装现有的固件版本" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "更新需要重启设备才能完成。" @@ -682,12 +680,6 @@ msgstr "概览" msgid "Target" msgstr "目标" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "fwupd 的进程作为一个简单的守护程序,可以让会话软件更新您本地机器的设备固件。它为桌面环境设计,但该项目可以应用在手机、平板电脑和服务器上。" - -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "本项目的目标是让 Linux 上更新固件的流程变得自动化、安全又可靠。您既可以使用如 GNOME 软件这样的软件包管理器查看和应用更新,也可以直接使用命令行工具或者 D-Bus 接口。" - #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" msgstr "标题" @@ -743,9 +735,6 @@ msgstr "更新概览" msgid "Update Version" msgstr "更新版本" -msgid "Update device firmware on Linux" -msgstr "更新 Linux 上的设备固件" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "更新失败为已知问题,请访问此 URL 以获取详情:" @@ -819,6 +808,3 @@ 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 "您的发行商可能尚未认证任何固件的系统及设备兼容性。" - -msgid "fwupd" -msgstr "fwupd" diff --git a/po/zh_TW.po b/po/zh_TW.po index 3b68485ec..3f13962a3 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2018-10-12 11:10+0000\n" -"Last-Translator: Richard Hughes \n" "Language-Team: Chinese (Taiwan) (http://www.transifex.com/freedesktop/fwupd/language/zh_TW/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -53,7 +51,7 @@ msgstr "允許降級韌體版本" msgid "Allow re-installing existing firmware versions" msgstr "允許重新安裝既有的韌體版本" -#. TRANSLATORS: explain why we want to upload +#. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "有更新必須重新開機才能完成。" @@ -472,12 +470,6 @@ msgstr "安裝未簽署的裝置韌體" msgid "Install unsigned system firmware" msgstr "安裝未簽署的系統韌體" -#. show message in progressbar -#. TRANSLATORS: %1 is a device name -#, c-format -msgid "Installing %s" -msgstr "正安裝 %s" - #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "安裝韌體更新中…" @@ -793,20 +785,10 @@ msgstr "目標" 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(Linux 廠商韌體服務,Linux Vendor Firmware Service)是由獨立法人運作的免費服務,而與 $OS_RELEASE:NAME$ 沒有關聯。您的系統散布商可能尚未驗證過任何韌體更新與您系統間或連接裝置上的相容性。所有本服務中的韌體僅由原始設備製造商提供。" -msgid "The fwupd process is a simple daemon to allow session software to update device firmware on your local machine. It is designed for desktops, but this project is also usable on phones, tablets and on headless servers." -msgstr "fwupd 程序是個簡易幕後程式,允許工作階段軟體更新您本地端機器上的裝置韌體。它主要設計給桌面電腦使用,但本專案也能用於手機、平板,或是指令列介面伺服器。" - -#. TRANSLATORS: exactly one update needs this -msgid "The update requires a reboot to complete." -msgstr "更新必須重新開機才能完成。" - #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "此程式僅有 root 身份才能正常運作" -msgid "This project aims to make updating firmware on Linux automatic, safe and reliable. You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the D-Bus interface directly." -msgstr "本專案宗旨在於讓 Linux 上的韌體更新能自動、安全、可靠。您可以使用圖形介面的軟體管理員,如《GNOME 軟體》來檢視並套用更新,或用指令列工具,或甚至直接用 D-Bus 介面。" - 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 "這個遠端站點包含未列入禁運,但仍處於硬體廠商測試階段的韌體。您應該確保自己在韌體更新失敗時有方法能夠手動降級韌體。" @@ -873,9 +855,6 @@ msgstr "更新摘要" msgid "Update Version" msgstr "更新版本" -msgid "Update device firmware on Linux" -msgstr "在 Linux 上更新裝置韌體" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "更新失敗是已知議題,請造訪此 URL 瞭解更多資訊:" @@ -953,6 +932,3 @@ 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 "您的系統散布商可能尚未驗證過任何韌體更新與您系統間或連接裝置上的相容性。" - -msgid "fwupd" -msgstr "fwupd" diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml deleted file mode 120000 index 25d9b6844..000000000 --- a/snap/snapcraft.yaml +++ /dev/null @@ -1 +0,0 @@ -../contrib/snap/snapcraft-stable.yaml \ No newline at end of file diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 000000000..50512dd27 --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,292 @@ +name: fwupd +version-script: cat $SNAPCRAFT_STAGE/version +version: 'daily' +summary: A standalone version of fwupd to install newer firmware updates +description: | + This is a tool that can be used to install firmware updates on devices + not yet supported by the version of fwupd distributed with the OS. + +grade: stable +confinement: classic + +architectures: + - amd64 + +apps: + dfu-tool: + command: dfu-tool.wrapper + fwupdtool: + command: fwupdtool.wrapper + completer: + share/bash-completion/completions/fwupdtool + fwupd: + command: fwupd.wrapper + daemon: simple + fwupdmgr: + command: fwupdmgr.wrapper + completer: + share/bash-completion/completions/fwupdmgr + +parts: + libefivar-dev: + plugin: make + make-parameters: + - prefix=/ + - libdir=/lib + source: https://github.com/rhboot/efivar/releases/download/37/efivar-37.tar.bz2 + build-packages: + - libpopt-dev + prime: + - -include + - -bin + - -share/man + - -lib/pkgconfig + #adjust the paths from libefivar + libefivar-fixpkgconfig: + plugin: make + source: contrib/snap/libefivar-fixpkgconfig + make-parameters: + - SNAPCRAFT_STAGE=$SNAPCRAFT_STAGE + after: [libefivar-dev] + libsmbios: + plugin: autotools + source: https://github.com/dell/libsmbios/archive/v2.4.2.tar.gz + build-packages: + - libxml2-dev + - pkg-config + - autoconf + - automake + - libtool + - autopoint + prime: + - -include/ + - -lib/pkgconfig + - -lib/python3.5 + - -sbin/ + - -share/ + - -etc/ + - -lib/*.a + meson: + plugin: python + source: https://github.com/mesonbuild/meson/releases/download/0.47.2/meson-0.47.2.tar.gz + build-packages: + - ninja-build + prime: + - -bin + - -etc + - -lib + - -share + - -usr + gudev: + plugin: autotools + source: https://github.com/GNOME/libgudev/archive/232.tar.gz + configflags: + - --disable-umockdev + build-packages: + - libglib2.0-dev + - pkg-config + - libudev-dev + - gtk-doc-tools + - gnome-common + prime: + - -include + - -lib/girepository-1.0 + - -lib/pkgconfig + - -share/ + libusb: + plugin: autotools + source: https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2 + configflags: + - --disable-static + prime: + - -include/ + - -lib/pkgconfig + gusb: + plugin: meson + source: https://github.com/hughsie/libgusb/archive/0.3.0.tar.gz + meson-parameters: [--prefix=/, + -Dtests=false, + -Dvapi=false, + -Ddocs=false] + build-packages: + - libgirepository1.0-dev + prime: + - -bin/ + - -include + - -share + - -lib/*/pkgconfig + - -lib/*/girepository-1.0 + after: [meson, libusb] + gnu-efi: + plugin: make + source: http://superb-dca2.dl.sourceforge.net/project/gnu-efi/gnu-efi-3.0.5.tar.bz2 + make-parameters: + - PREFIX=/usr + make-install-var: INSTALLROOT + prime: + - -usr/include/ + - -usr/lib + #fetch the latest version of the signed bootloader + #this might not match our fwupdx64.efi, but it's better than nothing + fwup-efi-signed: + build-packages: + - python3-apt + plugin: make + source: contrib/snap/fwup-efi-signed + #needed for UEFI plugin to build UX labels + build-introspection: + plugin: nil + stage-packages: + - python3-gi + - python3-gi-cairo + - python3-pil + prime: + - -etc + - -usr + - -lib + - -var + #0.19.8.1 adds support for GETTEXTDATADIRS which is needed by meson's msgfmthelper + gettext: + source: https://ftp.gnu.org/pub/gnu/gettext/gettext-0.19.8.1.tar.xz + plugin: autotools + build-packages: + - bison + - libunistring-dev + - libxml2-dev + configflags: + - --prefix=/usr + - --disable-static + - --disable-curses + - --disable-java + - --enable-relocatable + - --without-emacs + - --without-included-glib + - --without-included-libunistring + - --without-included-libxml + stage-packages: + - libunistring0 + - libxml2 + - libgomp1 + prime: + - -**/*.a + - -**/*.la + - -usr/bin + - -usr/include + - -usr/lib/gettext + - -usr/share + fwupd: + plugin: meson + meson-parameters: [--prefix=/, + -Defi-includedir=$SNAPCRAFT_STAGE/usr/include/efi, + -Defi-ldsdir=$SNAPCRAFT_STAGE/usr/lib, + -Defi-libdir=$SNAPCRAFT_STAGE/usr/lib, + -Dtests=false, + -Ddaemon=true, + -Dgtkdoc=false, + -Dintrospection=false, + -Dman=false, + -Dudevdir=$SNAPCRAFT_STAGE/lib/udev, + "-Dlibxmlb:gtkdoc=false", + "-Dlibxmlb:introspection=false", + -Dpkcs7=false] + source: . + source-type: git + override-build: | + snapcraftctl build + echo $(git describe HEAD --always) > $SNAPCRAFT_STAGE/version + build-packages: + - bash-completion + - gcab + - gnutls-dev + - libarchive-dev + - libcairo-dev + - libelf-dev + - libgcab-dev + - libglib2.0-dev + - libgpgme11-dev + - libjson-glib-dev + - libpango1.0-dev + - libpolkit-gobject-1-dev + - libsoup2.4-dev + - libsqlite3-dev + - locales + - pkg-config + - uuid-dev + stage-packages: + - libgcab-1.0-0 + - libarchive13 + - libassuan0 + - liblcms2-2 + - libelf1 + - libgpgme11 + - libjson-glib-1.0-0 + - libpolkit-gobject-1-0 + - libsoup2.4-1 + - glib-networking + - libglib2.0-bin + prime: + # we explicitly don't want /usr/bin/gpgconf + # this will cause gpgme to error finding it + # but that also avoids trying to use non-existent + # /usr/bin/gpg2 + - -usr/bin + - -usr/sbin + - -usr/share/man + - -usr/share/GConf + - -etc/X11 + - -etc/ldap + - -etc/logcheck + - -usr/lib/dconf + - -usr/lib/gcc + - -usr/lib/glib-networking + - -usr/lib/gnupg2 + - -usr/lib/sasl2 + - -usr/lib/systemd + - -usr/lib/*/audit + - -usr/share/glib-2.0/schemas + - -usr/share/X11 + - -include + - -lib/systemd + - -lib/udev + - -lib/*/pkgconfig + - -usr/share/lintian + - -usr/share/pkgconfig + - -usr/share/installed-tests + - -usr/share/polkit-1 + - -usr/share/vala + - -usr/share/doc + - -usr/share/gnupg2 + - -usr/share/info + - -usr/share/gir-1.0 + - -usr/share/upstart + - -usr/lib/*/pkgconfig + after: [gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext] + fix-bash-completion: + plugin: make + source: contrib/snap/fix-bash-completion + after: [fwupd] + update-mime: + plugin: make + source: contrib/snap/update-mime + stage-packages: + - shared-mime-info + - gsettings-desktop-schemas + - libxml2 + prime: + - -usr/bin + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/man + - -usr/share/lintian + - -usr/share/pkgconfig + - -usr/share/GConf + after: [fwupd] + fwupd-wrappers: + plugin: dump + source: contrib/snap + stage: + - dfu-tool.wrapper + - fwupd-command + - fwupdtool.wrapper + - fwupd.wrapper + - fwupdmgr.wrapper diff --git a/src/README.md b/src/README.md new file mode 100644 index 000000000..63423e78b --- /dev/null +++ b/src/README.md @@ -0,0 +1,137 @@ +Quirk use +--------- +Quirks are defined by creating an INI style file in the compiled in quirk location (typically `/usr/share/fwupd/quirks.d`). + +The quirk is declared by creating a group based upon the `DeviceInstanceId` or `GUID` +and then mapping out values to keys. + +## All plugins +All fwupd devices support the following quirks: + +### Plugin +Sets the plugin to use for a specific hardware device. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the plugin name, e.g. `csr` +* Minimum fwupd version: **1.1.0** +### UefiVersionFormat +Assigns the version format to use for a specific manufacturer. A specific version +format is sometimes chosen to match the appearance of other systems or +specifications. +* Key: a %FU_HWIDS_KEY_MANUFACTURER, e.g. `Alienware` +* Value: the version format, e.g. `none` +* Supported values: `none`, `use-triplet` +* Minimum fwupd version: **1.0.1** +### ComponentIDs +Assigns the version format to use for a specific AppStream component. A specific +version format is sometimes chosen to match the appearance of other systems or +specifications. +* Key: the optionally wildcarded AppStream ID e.g. `com.dell.uefi*.firmware` +* Value: the version format, e.g. `none` +* Minimum fwupd version: **1.0.1** +### Flags +Assigns optional quirks to use for a 8bitdo device +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the quirk, e.g. `is-bootloader` +* Supported values: + * `none`: no device quirks + * `is-bootloader`: device is in bootloader mode +* Minimum fwupd version: **1.0.3** +### Summary +Sets a summary for a specific hardware device. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* the device summary, e.g. `An open source display colorimeter` +* Minimum fwupd version: **1.0.2** +### Icon +Adds an icon name for a specific hardware device. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the device icon name, e.g. `media-removable` +* Minimum fwupd version: **1.0.2** +### Name +Sets a name for a specific hardware device. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the device name, e.g. `ColorHug` +* Minimum fwupd version: **1.0.2** +### Guid +Adds an extra GUID for a specific hardware device. If the value provided is not +already a suitable GUID, it will be converted to one. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the GUID, e.g. `537f7800-8529-5656-b2fa-b0901fe91696` +* Minimum fwupd version: **1.0.3** +### CounterpartGuid +Adds an counterpart GUID for a specific hardware device. If the value provided +is not already a suitable GUID, it will be converted to one. A counterpart +GUID is typically the GUID of the same device in bootloader or runtime mode, +if they have a different device PCI or USB ID. Adding this type of GUID does +not cause a "cascade" by matching using the quirk database. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the GUID, e.g. `537f7800-8529-5656-b2fa-b0901fe91696` +* Minimum fwupd version: **1.1.2** +### ParentGuid +Adds an extra GUID to mark as the parent device. If the value provided is not +already a suitable GUID, it will be converted to one. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the GUID, e.g. `537f7800-8529-5656-b2fa-b0901fe91696` +* Minimum fwupd version: **1.1.2** +### Children +Adds one or more virtual devices to a physical device. To set the object type +of the child device use a pipe before the object type, for instance: +`FuRts54xxDeviceUSB\VID_0763&PID_2806&I2C_01` If the type of device is not +specified the parent device type is used. If the values provided are not +already suitable GUIDs, they will be converted. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: The virtual device, delimited by a comma +* Minimum fwupd version: **1.1.2** +### Vendor +Sets a vendor name for a specific hardware device. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the vendor, e.g. `Hughski Limited` +* Minimum fwupd version: **1.0.3** +### VendorId +Sets a vendor ID for a specific hardware device. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: the vendor, e.g. `USB:0x123A` +* Minimum fwupd version: **1.1.2** +### Version +Sets a version for a specific hardware device. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: Version number, e.g. `1.2` +* Minimum fwupd version: **1.0.3** +### FirmwareSizeMin +Sets the minimum allowed firmware size. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: A number in bytes, e.g. `512` +* Minimum fwupd version: **1.1.2** +### FirmwareSizeMax +Sets the maximum allowed firmware size. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: A number in bytes, e.g. `1024` +* Minimum fwupd version: **1.1.2** +### InstallDuration +Sets the estimated time to flash the device +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: A number in seconds, e.g. `60` +* Minimum fwupd version: **1.1.3** +### VersionFormat +Sets the version format the device should use for conversion. +* Key: the device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` +* Value: The quirk format, e.g. `quad` +* Minimum fwupd version: **1.2.0** + +## Plugin specific +Plugins may add support for additional quirks that are relevant only for +those plugins. View them by looking at the `README.md` in plugin directories. + +## Example +Here is an example as seen in the CSR plugin. + +``` +[DeviceInstanceId=USB\VID_0A12&PID_1337] +Plugin = csr +Name = H05 +Summary = Bluetooth Headphones +Icon = audio-headphones +Vendor = AIAIAI +[DeviceInstanceId=USB\VID_0A12&PID_1337&REV_2520] +Version = 1.2 +``` +Additional samples can be found in other plugins. diff --git a/src/fu-archive.c b/src/fu-archive.c new file mode 100644 index 000000000..ac68a0ca7 --- /dev/null +++ b/src/fu-archive.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuArchive" + +#include "config.h" + +#include +#include +#include + +#include "fu-archive.h" + +/** + * SECTION:fu-archive + * @title: FuArchive + * @short_description: an in-memory archive decompressor + */ + +struct _FuArchive { + GObject parent_instance; + GHashTable *entries; +}; + +G_DEFINE_TYPE (FuArchive, fu_archive, G_TYPE_OBJECT) + +static void +fu_archive_finalize (GObject *obj) +{ + FuArchive *self = FU_ARCHIVE (obj); + + g_hash_table_unref (self->entries); + G_OBJECT_CLASS (fu_archive_parent_class)->finalize (obj); +} + +static void +fu_archive_class_init (FuArchiveClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_archive_finalize; +} + +static void +fu_archive_init (FuArchive *self) +{ + self->entries = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_bytes_unref); +} + +/** + * fu_archive_lookup_by_fn: + * @self: A #FuArchive + * @fn: A filename + * @error: A #GError, or %NULL + * + * Finds the blob referenced by filename + * + * Returns: (transfer none): a #GBytes, or %NULL if the filename was not found + **/ +GBytes * +fu_archive_lookup_by_fn (FuArchive *self, const gchar *fn, GError **error) +{ + GBytes *fw; + + g_return_val_if_fail (FU_IS_ARCHIVE (self), NULL); + g_return_val_if_fail (fn != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + fw = g_hash_table_lookup (self->entries, fn); + if (fw == NULL) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no blob for %s", fn); + } + return fw; +} + +/** + * fu_archive_iterate: + * @self: A #FuArchive + * @callback: A #FuArchiveIterateFunc. + * @user_data: User data. + * + * Iterates over the archive contents, calling the given function for each + * of the files found. + */ +void +fu_archive_iterate (FuArchive *self, FuArchiveIterateFunc callback, gpointer user_data) +{ + GHashTableIter iter; + gpointer key, value; + + g_return_if_fail (FU_IS_ARCHIVE (self)); + g_return_if_fail (callback != NULL); + + g_hash_table_iter_init (&iter, self->entries); + while (g_hash_table_iter_next (&iter, &key, &value)) + callback (self, (const gchar *)key, (GBytes *)value, user_data); +} + +/* workaround the struct types of libarchive */ +typedef struct archive _archive_read_ctx; + +static void +_archive_read_ctx_free (_archive_read_ctx *arch) +{ + archive_read_close (arch); + archive_read_free (arch); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(_archive_read_ctx, _archive_read_ctx_free) + +static gboolean +fu_archive_load (FuArchive *self, GBytes *blob, FuArchiveFlags flags, GError **error) +{ + int r; + g_autoptr(_archive_read_ctx) arch = NULL; + + /* decompress anything matching either glob */ + arch = archive_read_new (); + if (arch == NULL) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "libarchive startup failed"); + return FALSE; + } + archive_read_support_format_all (arch); + archive_read_support_filter_all (arch); + r = archive_read_open_memory (arch, + (void *) g_bytes_get_data (blob, NULL), + (size_t) g_bytes_get_size (blob)); + if (r != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot open: %s", + archive_error_string (arch)); + return FALSE; + } + while (TRUE) { + const gchar *fn; + gint64 bufsz; + gssize rc; + struct archive_entry *entry; + g_autofree gchar *fn_key = NULL; + g_autofree guint8 *buf = NULL; + + r = archive_read_next_header (arch, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot read header: %s", + archive_error_string (arch)); + return FALSE; + } + + /* only extract if valid */ + fn = archive_entry_pathname (entry); + if (fn == NULL) + continue; + bufsz = archive_entry_size (entry); + if (bufsz > 1024 * 1024 * 1024) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot read huge files"); + return FALSE; + } + buf = g_malloc (bufsz); + rc = archive_read_data (arch, buf, (gsize) bufsz); + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot read data: %s", + archive_error_string (arch)); + return FALSE; + } + if (rc != bufsz) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "read %" G_GSSIZE_FORMAT " of %" G_GINT64_FORMAT, + rc, bufsz); + return FALSE; + } + if (flags & FU_ARCHIVE_FLAG_IGNORE_PATH) { + fn_key = g_path_get_basename (fn); + } else { + fn_key = g_strdup (fn); + } + g_debug ("adding %s [%" G_GINT64_FORMAT "]", fn_key, bufsz); + g_hash_table_insert (self->entries, + g_steal_pointer (&fn_key), + g_bytes_new_take (g_steal_pointer (&buf), bufsz)); + } + + /* success */ + return TRUE; +} + +/** + * fu_archive_new: + * @data: A #GBytes + * @flags: A #FuArchiveFlags, e.g. %FU_ARCHIVE_FLAG_NONE + * @error: A #GError, or %NULL + * + * Parses @data as an archive and decompresses all files to memory blobs. + * + * Returns: a #FuArchive, or %NULL if the archive was invalid in any way. + **/ +FuArchive * +fu_archive_new (GBytes *data, FuArchiveFlags flags, GError **error) +{ + g_autoptr(FuArchive) self = g_object_new (FU_TYPE_ARCHIVE, NULL); + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + if (!fu_archive_load (self, data, flags, error)) + return NULL; + return g_steal_pointer (&self); +} diff --git a/src/fu-archive.h b/src/fu-archive.h new file mode 100644 index 000000000..90a9db2a2 --- /dev/null +++ b/src/fu-archive.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_ARCHIVE_H +#define __FU_ARCHIVE_H + +#include + +G_BEGIN_DECLS + +#define FU_TYPE_ARCHIVE (fu_archive_get_type ()) + +G_DECLARE_FINAL_TYPE (FuArchive, fu_archive, FU, ARCHIVE, GObject) + +/** + * FwupdError: + * @FU_ARCHIVE_FLAG_NONE: No flags set + * @FU_ARCHIVE_FLAG_IGNORE_PATH: Ignore any path component + * + * The flags to use when loading the archive. + **/ +typedef enum { + FU_ARCHIVE_FLAG_NONE = 0, + FU_ARCHIVE_FLAG_IGNORE_PATH = 1 << 0, + /*< private >*/ + FU_ARCHIVE_FLAG_LAST +} FuArchiveFlags; + +/** + * FuArchiveIterateFunc: + * @self: A #FuArchive. + * @filename: A filename. + * @blob: The blob referenced by @filename. + * @user_data: User data. + * + * Specifies the type of archive iteration function. + */ +typedef void (*FuArchiveIterateFunc) (FuArchive *self, + const gchar *filename, + GBytes *bytes, + gpointer user_data); + +FuArchive *fu_archive_new (GBytes *data, + FuArchiveFlags flags, + GError **error); +GBytes *fu_archive_lookup_by_fn (FuArchive *self, + const gchar *fn, + GError **error); +void fu_archive_iterate (FuArchive *self, + FuArchiveIterateFunc callback, + gpointer user_data); + +G_END_DECLS + +#endif /* __FU_ARCHIVE_H */ diff --git a/src/fu-common-cab.c b/src/fu-common-cab.c index 49fdf0a56..e1c747dee 100644 --- a/src/fu-common-cab.c +++ b/src/fu-common-cab.c @@ -15,7 +15,9 @@ #include "fwupd-error.h" -#ifdef HAVE_GCAB_1_0 +#ifndef HAVE_GCAB_1_0 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCabCabinet, g_object_unref) +#endif static GCabFile * _gcab_cabinet_get_file_by_name (GCabCabinet *cabinet, const gchar *basename) @@ -23,50 +25,82 @@ _gcab_cabinet_get_file_by_name (GCabCabinet *cabinet, const gchar *basename) GPtrArray *folders = gcab_cabinet_get_folders (cabinet); for (guint i = 0; i < folders->len; i++) { GCabFolder *cabfolder = GCAB_FOLDER (g_ptr_array_index (folders, i)); +#ifdef HAVE_GCAB_1_0 GCabFile *cabfile = gcab_folder_get_file_by_name (cabfolder, basename); if (cabfile != NULL) return cabfile; +#else + g_autoptr(GSList) files = gcab_folder_get_files (cabfolder); + for (GSList *l = files; l != NULL; l = l->next) { + GCabFile *cabfile = GCAB_FILE (l->data); + if (g_strcmp0 (gcab_file_get_name (cabfile), basename) == 0) + return cabfile; + } +#endif } return NULL; } -/* sets the firmware and signature blobs on AsRelease */ -static gboolean -fu_common_store_from_cab_release (AsRelease *release, GCabCabinet *cabinet, GError **error) +#ifndef HAVE_GCAB_1_0 +static GBytes * +_gcab_file_get_bytes (GCabFile *cabfile) +{ + GBytes *blob = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(GError) error_local = NULL; + + fn = g_build_filename (g_object_get_data (G_OBJECT (cabfile), + "fwupd::DecompressPath"), + gcab_file_get_extract_name (cabfile), + NULL); + blob = fu_common_get_contents_bytes (fn, &error_local); + if (blob == NULL) { + g_warning ("failed to read temp file: %s", error_local->message); + return NULL; + } + return blob; +} +#endif + +/* sets the firmware and signature blobs on XbNode */ +static gboolean +fu_common_store_from_cab_release (XbNode *release, GCabCabinet *cabinet, GError **error) { - AsChecksum *csum_tmp; GCabFile *cabfile; GBytes *blob; - guint64 size; - g_autofree gchar *basename = NULL; - g_autofree gchar *checksum = NULL; + const gchar *csum_filename = NULL; const gchar *suffixes[] = { "asc", "p7b", "p7c", NULL }; + g_autofree gchar *basename = NULL; + g_autofree gchar *release_key = NULL; + g_autoptr(XbNode) csum_tmp = NULL; + g_autoptr(XbNode) nsize = NULL; /* ensure we always have a content checksum */ - csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); - if (csum_tmp == NULL) { - g_autoptr(AsChecksum) csum = as_checksum_new (); - as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT); - /* if this isn't true, a firmware needs to set in - * the metainfo.xml file something like: - * */ - as_checksum_set_filename (csum, "firmware.bin"); - as_release_add_checksum (release, csum); - csum_tmp = csum; - } + csum_tmp = xb_node_query_first (release, "checksum[@target='content']", NULL); + if (csum_tmp != NULL) + csum_filename = xb_node_get_attr (csum_tmp, "filename"); + + /* if this isn't true, a firmware needs to set in the metainfo.xml file + * something like: */ + if (csum_filename == NULL) + csum_filename = "firmware.bin"; /* get the main firmware file */ - basename = g_path_get_basename (as_checksum_get_filename (csum_tmp)); + basename = g_path_get_basename (csum_filename); cabfile = _gcab_cabinet_get_file_by_name (cabinet, basename); if (cabfile == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "cannot find %s in archive", - as_checksum_get_filename (csum_tmp)); + basename); return FALSE; } +#ifdef HAVE_GCAB_1_0 blob = gcab_file_get_bytes (cabfile); +#else + blob = _gcab_file_get_bytes (cabfile); +#endif if (blob == NULL) { g_set_error_literal (error, FWUPD_ERROR, @@ -76,35 +110,41 @@ fu_common_store_from_cab_release (AsRelease *release, GCabCabinet *cabinet, GErr } /* set the blob */ - as_release_set_blob (release, basename, blob); + release_key = g_strdup_printf ("fwupd::ReleaseBlob(%s)", basename); + xb_node_set_data (release, release_key, blob); - /* set if unspecified, but error out if specified and incorrect */ - size = as_release_get_size (release, AS_SIZE_KIND_INSTALLED); - if (size == 0) { - as_release_set_size (release, AS_SIZE_KIND_INSTALLED, - g_bytes_get_size (blob)); - } else if (size != g_bytes_get_size (blob)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "contents size invalid, expected " - "%" G_GSIZE_FORMAT ", got %" G_GUINT64_FORMAT, - g_bytes_get_size (blob), size); - return FALSE; + /* set as metadata if unset, but error if specified and incorrect */ + nsize = xb_node_query_first (release, "size[@type='installed']", NULL); + if (nsize != NULL) { + guint64 size = fu_common_strtoull (xb_node_get_text (nsize)); + if (size != g_bytes_get_size (blob)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "contents size invalid, expected " + "%" G_GSIZE_FORMAT ", got %" G_GUINT64_FORMAT, + g_bytes_get_size (blob), size); + return FALSE; + } + } else { + guint64 size = g_bytes_get_size (blob); + g_autoptr(GBytes) blob_sz = g_bytes_new (&size, sizeof(guint64)); + xb_node_set_data (release, "fwupd::ReleaseSize", blob_sz); } /* set if unspecified, but error out if specified and incorrect */ - checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob); - if (as_checksum_get_value (csum_tmp) == NULL) { - as_checksum_set_value (csum_tmp, checksum); - } else if (g_strcmp0 (checksum, as_checksum_get_value (csum_tmp)) != 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "contents checksum invalid, expected %s, got %s", - checksum, - as_checksum_get_value (csum_tmp)); - return FALSE; + if (csum_tmp != NULL && xb_node_get_text (csum_tmp) != NULL) { + g_autofree gchar *checksum = NULL; + checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob); + if (g_strcmp0 (checksum, xb_node_get_text (csum_tmp)) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "contents checksum invalid, expected %s, got %s", + checksum, + xb_node_get_text (csum_tmp)); + return FALSE; + } } /* if the signing file exists, set that too */ @@ -113,7 +153,12 @@ fu_common_store_from_cab_release (AsRelease *release, GCabCabinet *cabinet, GErr basename_sig = g_strdup_printf ("%s.%s", basename, suffixes[i]); cabfile = _gcab_cabinet_get_file_by_name (cabinet, basename_sig); if (cabfile != NULL) { + g_autofree gchar *release_key_sig = NULL; +#ifdef HAVE_GCAB_1_0 blob = gcab_file_get_bytes (cabfile); +#else + blob = _gcab_file_get_bytes (cabfile); +#endif if (blob == NULL) { g_set_error (error, FWUPD_ERROR, @@ -122,7 +167,9 @@ fu_common_store_from_cab_release (AsRelease *release, GCabCabinet *cabinet, GErr basename_sig); return FALSE; } - as_release_set_blob (release, basename_sig, blob); + release_key_sig = g_strdup_printf ("fwupd::ReleaseBlob(%s)", + basename_sig); + xb_node_set_data (release, release_key_sig, blob); } } @@ -130,22 +177,24 @@ fu_common_store_from_cab_release (AsRelease *release, GCabCabinet *cabinet, GErr return TRUE; } -/* adds each GCabFile to the store */ +/* adds each GCabFile to the silo */ static gboolean -fu_common_store_from_cab_file (AsStore *store, GCabCabinet *cabinet, +fu_common_store_from_cab_file (XbBuilder *builder, GCabCabinet *cabinet, GCabFile *cabfile, GError **error) { GBytes *blob; - GPtrArray *releases; - g_autoptr(AsApp) app = NULL; g_autoptr(GError) error_local = NULL; -#if !AS_CHECK_VERSION(0,7,5) - g_autofree gchar *cache_fn = NULL; - g_autofree gchar *cachedir = NULL; -#endif + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + + /* rewrite to be under a components root */ + xb_builder_source_set_prefix (source, "components"); /* parse file */ +#ifdef HAVE_GCAB_1_0 blob = gcab_file_get_bytes (cabfile); +#else + blob = _gcab_file_get_bytes (cabfile); +#endif if (blob == NULL) { g_set_error_literal (error, FWUPD_ERROR, @@ -153,9 +202,10 @@ fu_common_store_from_cab_file (AsStore *store, GCabCabinet *cabinet, "no GBytes from GCabFile"); return FALSE; } - app = as_app_new (); -#if AS_CHECK_VERSION(0,7,5) - if (!as_app_parse_data (app, blob, AS_APP_PARSE_FLAG_NONE, &error_local)) { + if (!xb_builder_source_load_xml (source, + g_bytes_get_data (blob, NULL), + XB_BUILDER_SOURCE_FLAG_NONE, + &error_local)) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -163,54 +213,15 @@ fu_common_store_from_cab_file (AsStore *store, GCabCabinet *cabinet, error_local->message); return FALSE; } -#else - cachedir = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG); - cache_fn = g_build_filename (cachedir, gcab_file_get_extract_name (cabfile), NULL); - if (!fu_common_mkdir_parent (cache_fn, error)) - return FALSE; - if (!g_file_set_contents (cache_fn, g_bytes_get_data (blob, NULL), - g_bytes_get_size (blob), &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "could not save temporary MetaInfo XML to %s: %s", - cache_fn, error_local->message); - return FALSE; - } - if (!as_app_parse_file (app, cache_fn, AS_APP_PARSE_FLAG_NONE, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "could not parse MetaInfo XML: %s", - error_local->message); - return FALSE; - } -#endif - - /* process each listed release */ - releases = as_app_get_releases (app); - if (releases->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no releases in metainfo file"); - return FALSE; - } - for (guint i = 0; i < releases->len; i++) { - AsRelease *release = g_ptr_array_index (releases, i); - g_debug ("processing release: %s", as_release_get_version (release)); - if (!fu_common_store_from_cab_release (release, cabinet, error)) - return FALSE; - } + xb_builder_import_source (builder, source); /* success */ - as_store_add_app (store, app); return TRUE; } -/* adds each GCabFolder to the store */ +/* adds each GCabFolder to the silo */ static gboolean -fu_common_store_from_cab_folder (AsStore *store, GCabCabinet *cabinet, +fu_common_store_from_cab_folder (XbBuilder *builder, GCabCabinet *cabinet, GCabFolder *cabfolder, GError **error) { g_autoptr(GSList) cabfiles = gcab_folder_get_files (cabfolder); @@ -218,8 +229,8 @@ fu_common_store_from_cab_folder (AsStore *store, GCabCabinet *cabinet, GCabFile *cabfile = GCAB_FILE (l->data); const gchar *fn = gcab_file_get_extract_name (cabfile); g_debug ("processing file: %s", fn); - if (as_format_guess_kind (fn) == AS_FORMAT_KIND_METAINFO) { - if (!fu_common_store_from_cab_file (store, cabinet, cabfile, error)) { + if (g_str_has_suffix (fn, ".metainfo.xml")) { + if (!fu_common_store_from_cab_file (builder, cabinet, cabfile, error)) { g_prefix_error (error, "%s could not be loaded: ", gcab_file_get_extract_name (cabfile)); return FALSE; @@ -232,11 +243,12 @@ fu_common_store_from_cab_folder (AsStore *store, GCabCabinet *cabinet, typedef struct { guint64 size_total; guint64 size_max; + const gchar *decompress_path; GError *error; } FuCommonCabHelper; static gboolean -as_cab_store_file_cb (GCabFile *file, gpointer user_data) +fu_common_store_file_cb (GCabFile *file, gpointer user_data) { FuCommonCabHelper *helper = (FuCommonCabHelper *) user_data; g_autofree gchar *basename = NULL; @@ -279,34 +291,135 @@ as_cab_store_file_cb (GCabFile *file, gpointer user_data) /* ignore the dirname completely */ basename = g_path_get_basename (name); gcab_file_set_extract_name (file, basename); + +#ifndef HAVE_GCAB_1_0 + /* set this for old versions of GCab */ + g_object_set_data_full (G_OBJECT (file), + "fwupd::DecompressPath", + g_strdup (helper->decompress_path), + g_free); +#endif + + return TRUE; +} + +static gint +fu_common_cab_sort_cb (XbBuilderNode *bn1, XbBuilderNode *bn2, gpointer user_data) +{ + guint64 prio1 = xb_builder_node_get_attr_as_uint (bn1, "priority"); + guint64 prio2 = xb_builder_node_get_attr_as_uint (bn2, "priority"); + if (prio1 > prio2) + return -1; + if (prio1 < prio2) + return 1; + return 0; +} + +static gboolean +fu_common_cab_sort_priority_cb (XbBuilderFixup *self, + XbBuilderNode *bn, + gpointer user_data, + GError **error) +{ + xb_builder_node_sort_children (bn, fu_common_cab_sort_cb, user_data); + return TRUE; +} + +static XbBuilderNode * +_xb_builder_node_get_child_by_element_attr (XbBuilderNode *bn, + const gchar *element, + const gchar *attr_name, + const gchar *attr_value) +{ + GPtrArray *bcs = xb_builder_node_get_children (bn); + for (guint i = 0; i < bcs->len; i++) { + XbBuilderNode *bc = g_ptr_array_index (bcs, i); + if (g_strcmp0 (xb_builder_node_get_element (bc), element) != 0) + continue; + if (g_strcmp0 (xb_builder_node_get_attr (bc, "type"), "container") == 0) + return g_object_ref (bc); + } + return NULL; +} + +static gboolean +fu_common_cab_set_container_checksum_cb (XbBuilderFixup *self, + XbBuilderNode *bn, + gpointer user_data, + GError **error) +{ + + const gchar *container_checksum = (const gchar *) user_data; + g_autoptr(XbBuilderNode) csum = NULL; + + /* not us */ + if (g_strcmp0 (xb_builder_node_get_element (bn), "release") != 0) + return TRUE; + + /* verify it exists */ + csum = _xb_builder_node_get_child_by_element_attr (bn, "checksum", + "target", "container"); + if (csum == NULL) { + csum = xb_builder_node_insert (bn, "checksum", + "target", "container", + NULL); + } + + /* verify it is correct */ + if (g_strcmp0 (xb_builder_node_get_text (csum), container_checksum) != 0) { + g_debug ("invalid container checksum %s, fixing up to %s", + xb_builder_node_get_text (csum), container_checksum); + xb_builder_node_set_text (csum, container_checksum, -1); + } return TRUE; } /** - * fu_common_store_from_cab_bytes: + * fu_common_cab_build_silo: * @blob: A readable blob * @size_max: The maximum size of the archive * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN * - * Create an AppStream store from a cabinet archive. + * Create an AppStream silo from a cabinet archive. * - * Returns: a store, or %NULL on error + * Returns: a #XbSilo, or %NULL on error **/ -AsStore * -fu_common_store_from_cab_bytes (GBytes *blob, guint64 size_max, GError **error) +XbSilo * +fu_common_cab_build_silo (GBytes *blob, guint64 size_max, GError **error) { - FuCommonCabHelper helper = { 0 }; + FuCommonCabHelper helper = { + .size_total = 0, + .size_max = size_max, + .error = NULL, + }; GPtrArray *folders; - g_autoptr(AsStore) store = as_store_new (); + g_autofree gchar *container_checksum = NULL; +#ifndef HAVE_GCAB_1_0 + g_autofree gchar *tmp_path = NULL; + g_autoptr(GFile) tmp_file = NULL; +#endif + g_autoptr(XbSilo) silo = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderFixup) fixup = NULL; + g_autoptr(XbBuilderFixup) fixup2 = NULL; g_autoptr(GCabCabinet) cabinet = gcab_cabinet_new (); g_autoptr(GError) error_local = NULL; g_autoptr(GInputStream) ip = NULL; + g_autoptr(GPtrArray) components = NULL; + + /* sort the components by priority */ + fixup = xb_builder_fixup_new ("OrderByPriority", + fu_common_cab_sort_priority_cb, + NULL, NULL); + xb_builder_fixup_set_max_depth (fixup, 0); + xb_builder_add_fixup (builder, fixup); /* load from a seekable stream */ ip = g_memory_input_stream_new_from_bytes (blob); if (!gcab_cabinet_load (cabinet, ip, NULL, error)) return NULL; +#ifdef HAVE_GCAB_1_0 /* check the size is sane */ if (gcab_cabinet_get_size (cabinet) > size_max) { g_autofree gchar *sz_val = g_format_size (gcab_cabinet_get_size (cabinet)); @@ -320,9 +433,8 @@ fu_common_store_from_cab_bytes (GBytes *blob, guint64 size_max, GError **error) } /* decompress the file to memory */ - helper.size_max = size_max; if (!gcab_cabinet_extract_simple (cabinet, NULL, - as_cab_store_file_cb, &helper, + fu_common_store_file_cb, &helper, NULL, &error_local)) { g_set_error_literal (error, FWUPD_ERROR, @@ -330,6 +442,29 @@ fu_common_store_from_cab_bytes (GBytes *blob, guint64 size_max, GError **error) error_local->message); return NULL; } +#else + /* decompress to /tmp */ + tmp_path = g_dir_make_tmp ("fwupd-XXXXXX", &error_local); + if (tmp_path == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to create temp dir: %s", + error_local->message); + return NULL; + } + helper.decompress_path = tmp_path; + tmp_file = g_file_new_for_path (tmp_path); + if (!gcab_cabinet_extract_simple (cabinet, tmp_file, + fu_common_store_file_cb, &helper, + NULL, &error_local)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + error_local->message); + return NULL; + } +#endif /* the file callback set an error */ if (helper.error != NULL) { @@ -337,53 +472,65 @@ fu_common_store_from_cab_bytes (GBytes *blob, guint64 size_max, GError **error) return NULL; } + /* verbose profiling */ + if (g_getenv ("FWUPD_VERBOSE") != NULL) { + xb_builder_set_profile_flags (builder, + XB_SILO_PROFILE_FLAG_XPATH | + XB_SILO_PROFILE_FLAG_DEBUG); + } + /* look at each folder */ folders = gcab_cabinet_get_folders (cabinet); for (guint i = 0; i < folders->len; i++) { GCabFolder *cabfolder = GCAB_FOLDER (g_ptr_array_index (folders, i)); g_debug ("processing folder: %u/%u", i + 1, folders->len); - if (!fu_common_store_from_cab_folder (store, cabinet, cabfolder, error)) + if (!fu_common_store_from_cab_folder (builder, cabinet, cabfolder, error)) return NULL; } - /* did we get any valid AsApps */ - if (as_store_get_size (store) == 0) { - g_set_error_literal (error, + /* ensure the container checksum is always set */ + container_checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob); + fixup2 = xb_builder_fixup_new ("SetContainerChecksum", + fu_common_cab_set_container_checksum_cb, + container_checksum, NULL); + xb_builder_add_fixup (builder, fixup2); + + /* did we get any valid files */ + silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return NULL; + + components = xb_silo_query (silo, "components/component", 0, &error_local); + if (components == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "archive contained no valid metadata: %s", + error_local->message); + return NULL; + } + + /* process each listed release */ + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); + g_autoptr(GPtrArray) releases = NULL; + releases = xb_node_query (component, "releases/release", 0, &error_local); + if (releases == NULL) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "archive contained no valid metadata"); - return NULL; + "no releases in metainfo file: %s", + error_local->message); + return NULL; + } + for (guint j = 0; j < releases->len; j++) { + XbNode *rel = g_ptr_array_index (releases, j); + g_debug ("processing release: %s", xb_node_get_attr (rel, "version")); + if (!fu_common_store_from_cab_release (rel, cabinet, error)) + return NULL; + } } /* success */ - return g_steal_pointer (&store); + return g_steal_pointer (&silo); } - -#else - -AsStore * -fu_common_store_from_cab_bytes (GBytes *blob, guint64 size_max, GError **error) -{ - g_autoptr(AsStore) store = as_store_new (); - g_autoptr(GError) error_local = NULL; - - /* this is klunky as we have to write actual files to /tmp */ - if (!as_store_from_bytes (store, blob, NULL, &error_local)) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - error_local->message); - return NULL; - } - - /* did we get any valid AsApps */ - if (as_store_get_size (store) == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "archive contained no valid metadata"); - return NULL; - } - return g_steal_pointer (&store); -} -#endif diff --git a/src/fu-common-cab.h b/src/fu-common-cab.h index 28cf456f8..2f1da8d9f 100644 --- a/src/fu-common-cab.h +++ b/src/fu-common-cab.h @@ -7,9 +7,9 @@ #ifndef __FU_COMMON_CAB_H #define __FU_COMMON_CAB_H -#include +#include -AsStore *fu_common_store_from_cab_bytes (GBytes *blob, +XbSilo *fu_common_cab_build_silo (GBytes *blob, guint64 size_max, GError **error); diff --git a/src/fu-common-guid.c b/src/fu-common-guid.c new file mode 100644 index 000000000..088ddb4ed --- /dev/null +++ b/src/fu-common-guid.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include + +#include +#include + +#include "fwupd-error.h" + +#include "fu-common-guid.h" + +/** + * fu_common_guid_from_data: + * @namespace_id: A namespace ID, e.g. "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + * @data: data to hash + * @data_len: length of @data + * @error: A #GError or %NULL + * + * Returns a GUID for some data. This uses a hash and so even small + * differences in the @data will produce radically different return values. + * + * The implementation is taken from RFC4122, Section 4.1.3; specifically + * using a type-5 SHA-1 hash. + * + * Returns: A new GUID, or %NULL if the namespace_id was invalid + * + * Since: 1.2.0 + **/ +gchar * +fu_common_guid_from_data (const gchar *namespace_id, + const guint8 *data, + gsize data_len, + GError **error) +{ + gchar guid_new[37]; /* 36 plus NUL */ + gsize digestlen = 20; + guint8 hash[20]; + gint rc; + uuid_t uu_namespace; + uuid_t uu_new; + g_autoptr(GChecksum) csum = NULL; + + g_return_val_if_fail (namespace_id != NULL, NULL); + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (data_len != 0, NULL); + + /* convert the namespace to binary */ + rc = uuid_parse (namespace_id, uu_namespace); + if (rc != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "namespace '%s' is invalid", + namespace_id); + return NULL; + } + + /* hash the namespace and then the string */ + csum = g_checksum_new (G_CHECKSUM_SHA1); + g_checksum_update (csum, (guchar *) uu_namespace, 16); + g_checksum_update (csum, (guchar *) data, (gssize) data_len); + g_checksum_get_digest (csum, hash, &digestlen); + + /* copy most parts of the hash 1:1 */ + memcpy (uu_new, hash, 16); + + /* set specific bits according to Section 4.1.3 */ + uu_new[6] = (guint8) ((uu_new[6] & 0x0f) | (5 << 4)); + uu_new[8] = (guint8) ((uu_new[8] & 0x3f) | 0x80); + + /* return as a string */ + uuid_unparse (uu_new, guid_new); + return g_strdup (guid_new); +} + +/** + * fu_common_guid_is_valid: + * @guid: string to check + * + * Checks the source string is a valid string GUID descriptor. + * + * Returns: %TRUE if @guid was a valid GUID, %FALSE otherwise + * + * Since: 1.2.0 + **/ +gboolean +fu_common_guid_is_valid (const gchar *guid) +{ + gint rc; + uuid_t uu; + if (guid == NULL) + return FALSE; + if (strlen (guid) != 36) + return FALSE; + rc = uuid_parse (guid, uu); + if (uuid_is_null (uu)) + return FALSE; + return rc == 0; +} + +/** + * fu_common_guid_from_string: + * @str: A source string to use as a key + * + * Returns a GUID for a given string. This uses a hash and so even small + * differences in the @str will produce radically different return values. + * + * The implementation is taken from RFC4122, Section 4.1.3; specifically + * using a type-5 SHA-1 hash with a DNS namespace. + * The same result can be obtained with this simple python program: + * + * #!/usr/bin/python + * import uuid + * print uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') + * + * Returns: A new GUID, or %NULL if the string was invalid + * + * Since: 1.2.0 + **/ +gchar * +fu_common_guid_from_string (const gchar *str) +{ + if (str == NULL) + return NULL; + return fu_common_guid_from_data ("6ba7b810-9dad-11d1-80b4-00c04fd430c8", + (const guint8 *) str, strlen (str), NULL); +} diff --git a/src/fu-common-guid.h b/src/fu-common-guid.h new file mode 100644 index 000000000..db6e50fa9 --- /dev/null +++ b/src/fu-common-guid.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_COMMON_GUID_H__ +#define __FU_COMMON_GUID_H__ + +#include + +gboolean fu_common_guid_is_valid (const gchar *guid); +gchar *fu_common_guid_from_string (const gchar *str); +gchar *fu_common_guid_from_data (const gchar *namespace_id, + const guint8 *data, + gsize data_len, + GError **error); + +#endif /* __FU_COMMON_GUID_H__ */ diff --git a/src/fu-common-version.c b/src/fu-common-version.c new file mode 100644 index 000000000..fd8869a27 --- /dev/null +++ b/src/fu-common-version.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include + +#include + +#include "fwupd-error.h" + +#include "fu-common-version.h" + +#define FU_COMMON_VERSION_DECODE_BCD(val) ((((val) >> 4) & 0x0f) * 10 + ((val) & 0x0f)) + +/** + * fu_common_version_format_from_string: + * @str: A string, e.g. `quad` + * + * Converts text to a display version type. + * + * Returns: A #FuVersionFormat, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Since: 1.2.0 + **/ +FuVersionFormat +fu_common_version_format_from_string (const gchar *str) +{ + if (g_strcmp0 (str, "triplet") == 0) + return FU_VERSION_FORMAT_TRIPLET; + if (g_strcmp0 (str, "quad") == 0) + return FU_VERSION_FORMAT_QUAD; + if (g_strcmp0 (str, "intel-me2") == 0) + return FU_VERSION_FORMAT_INTEL_ME2; + if (g_strcmp0 (str, "bcd") == 0) + return FU_VERSION_FORMAT_BCD; + if (g_strcmp0 (str, "plain") == 0) + return FU_VERSION_FORMAT_PLAIN; + if (g_strcmp0 (str, "intel-me") == 0) + return FU_VERSION_FORMAT_INTEL_ME; + return FU_VERSION_FORMAT_QUAD; +} + +/** + * fu_common_version_format_to_string: + * @str: A #FuVersionFormat, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Converts a display version type to text. + * + * Returns: A string, e.g. `quad`, or %NULL if not known + * + * Since: 1.2.0 + **/ +const gchar * +fu_common_version_format_to_string (FuVersionFormat kind) +{ + if (kind == FU_VERSION_FORMAT_TRIPLET) + return "triplet"; + if (kind == FU_VERSION_FORMAT_QUAD) + return "quad"; + if (kind == FU_VERSION_FORMAT_INTEL_ME2) + return "intel-me2"; + if (kind == FU_VERSION_FORMAT_BCD) + return "bcd"; + if (kind == FU_VERSION_FORMAT_PLAIN) + return "plain"; + if (kind == FU_VERSION_FORMAT_INTEL_ME) + return "intel-me"; + return NULL; +} + +/** + * fu_common_version_from_uint32: + * @val: A uint32le version number + * @kind: version kind used for formatting, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Returns a dotted decimal version string from a 32 bit number. + * + * Returns: A version number, e.g. "1.0.3", or %NULL if not supported + * + * Since: 1.2.0 + **/ +gchar * +fu_common_version_from_uint32 (guint32 val, FuVersionFormat kind) +{ + if (kind == FU_VERSION_FORMAT_QUAD) { + /* AA.BB.CC.DD */ + return g_strdup_printf ("%u.%u.%u.%u", + (val >> 24) & 0xff, + (val >> 16) & 0xff, + (val >> 8) & 0xff, + val & 0xff); + } + if (kind == FU_VERSION_FORMAT_TRIPLET) { + /* AA.BB.CCDD */ + return g_strdup_printf ("%u.%u.%u", + (val >> 24) & 0xff, + (val >> 16) & 0xff, + val & 0xffff); + } + if (kind == FU_VERSION_FORMAT_PAIR) { + /* AABB.CCDD */ + return g_strdup_printf ("%u.%u", + (val >> 16) & 0xffff, + val & 0xffff); + } + if (kind == FU_VERSION_FORMAT_PLAIN) { + /* AABBCCDD */ + return g_strdup_printf ("%" G_GUINT32_FORMAT, val); + } + if (kind == FU_VERSION_FORMAT_BCD) { + /* AA.BB.CC.DD, but BCD */ + return g_strdup_printf ("%u.%u.%u.%u", + FU_COMMON_VERSION_DECODE_BCD(val >> 24), + FU_COMMON_VERSION_DECODE_BCD(val >> 16), + FU_COMMON_VERSION_DECODE_BCD(val >> 8), + FU_COMMON_VERSION_DECODE_BCD(val)); + } + if (kind == FU_VERSION_FORMAT_INTEL_ME) { + /* aaa+11.bbbbb.cccccccc.dddddddddddddddd */ + return g_strdup_printf ("%u.%u.%u.%u", + ((val >> 29) & 0x07) + 0x0b, + (val >> 24) & 0x1f, + (val >> 16) & 0xff, + val & 0xffff); + } + if (kind == FU_VERSION_FORMAT_INTEL_ME2) { + /* A.B.CC.DDDD */ + return g_strdup_printf ("%u.%u.%u.%u", + (val >> 28) & 0x0f, + (val >> 24) & 0x0f, + (val >> 16) & 0xff, + val & 0xffff); + } + return NULL; +} + +/** + * fu_common_version_from_uint16: + * @val: A uint16le version number + * @kind: version kind used for formatting, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Returns a dotted decimal version string from a 16 bit number. + * + * Returns: A version number, e.g. "1.3", or %NULL if not supported + * + * Since: 1.2.0 + **/ +gchar * +fu_common_version_from_uint16 (guint16 val, FuVersionFormat kind) +{ + if (kind == FU_VERSION_FORMAT_BCD) { + return g_strdup_printf ("%i.%i", + FU_COMMON_VERSION_DECODE_BCD(val >> 8), + FU_COMMON_VERSION_DECODE_BCD(val)); + } + if (kind == FU_VERSION_FORMAT_PAIR) { + return g_strdup_printf ("%u.%u", + (guint) (val >> 8) & 0xff, + (guint) val & 0xff); + } + if (kind == FU_VERSION_FORMAT_PLAIN) { + return g_strdup_printf ("%" G_GUINT16_FORMAT, val); + } + return NULL; +} + +static gint +fu_common_vercmp_char (gchar chr1, gchar chr2) +{ + if (chr1 == chr2) + return 0; + if (chr1 == '~') + return -1; + if (chr2 == '~') + return 1; + return chr1 < chr2 ? -1 : 1; +} + +static gint +fu_common_vercmp_chunk (const gchar *str1, const gchar *str2) +{ + guint i; + + /* trivial */ + if (g_strcmp0 (str1, str2) == 0) + return 0; + if (str1 == NULL) + return 1; + if (str2 == NULL) + return -1; + + /* check each char of the chunk */ + for (i = 0; str1[i] != '\0' && str2[i] != '\0'; i++) { + gint rc = fu_common_vercmp_char (str1[i], str2[i]); + if (rc != 0) + return rc; + } + return fu_common_vercmp_char (str1[i], str2[i]); +} + +static gboolean +_g_ascii_is_digits (const gchar *str) +{ + g_return_val_if_fail (str != NULL, FALSE); + for (gsize i = 0; str[i] != '\0'; i++) { + if (!g_ascii_isdigit (str[i])) + return FALSE; + } + return TRUE; +} + +/** + * fu_common_version_parse: + * @version: A version number + * + * Returns a dotted decimal version string from a version string. The supported + * formats are: + * + * - Dotted decimal, e.g. "1.2.3" + * - Base 16, a hex number *with* a 0x prefix, e.g. "0x10203" + * - Base 10, a string containing just [0-9], e.g. "66051" + * - Date in YYYYMMDD format, e.g. 20150915 + * + * Anything with a '.' or that doesn't match [0-9] or 0x[a-f,0-9] is considered + * a string and returned without modification. + * + * Returns: A version number, e.g. "1.0.3" + * + * Since: 1.2.0 + */ +gchar * +fu_common_version_parse (const gchar *version) +{ + const gchar *version_noprefix = version; + gchar *endptr = NULL; + guint64 tmp; + guint base; + + /* already dotted decimal */ + if (g_strstr_len (version, -1, ".") != NULL) + return g_strdup (version); + + /* is a date */ + if (g_str_has_prefix (version, "20") && + strlen (version) == 8) + return g_strdup (version); + + /* convert 0x prefixed strings to dotted decimal */ + if (g_str_has_prefix (version, "0x")) { + version_noprefix += 2; + base = 16; + } else { + /* for non-numeric content, just return the string */ + if (!_g_ascii_is_digits (version)) + return g_strdup (version); + base = 10; + } + + /* convert */ + tmp = g_ascii_strtoull (version_noprefix, &endptr, base); + if (endptr != NULL && endptr[0] != '\0') + return g_strdup (version); + if (tmp == 0) + return g_strdup (version); + return fu_common_version_from_uint32 ((guint32) tmp, FU_VERSION_FORMAT_TRIPLET); +} + +/** + * fu_common_version_guess_format: + * @version: A version number, e.g. "1.2.3" + * + * Guesses the version format from the version number. This is only a heuristic + * and plugins and components should explicitly set the version format whenever + * possible. + * + * If the version format cannot be guessed with any degree of accuracy, the + * %FU_VERSION_FORMAT_UNKNOWN constant is returned. + * + * Returns: A #FuVersionFormat, e.g. %FU_VERSION_FORMAT_QUAD + * + * Since: 1.2.0 + */ +FuVersionFormat +fu_common_version_guess_format (const gchar *version) +{ + guint sz; + g_auto(GStrv) split = NULL; + + /* nothing to use */ + if (version == NULL || version[0] == '\0') + return FU_VERSION_FORMAT_UNKNOWN; + + /* no dots, assume just text */ + split = g_strsplit (version, ".", -1); + sz = g_strv_length (split); + if (sz == 1) + return FU_VERSION_FORMAT_PLAIN; + + /* check for only-digit semver version */ + for (guint i = 0; split[i] != NULL; i++) { + /* check sections are plain numbers */ + if (!_g_ascii_is_digits (split[i])) + return FU_VERSION_FORMAT_UNKNOWN; + } + + /* the most common formats */ + if (sz == 2) + return FU_VERSION_FORMAT_PAIR; + if (sz == 3) + return FU_VERSION_FORMAT_TRIPLET; + if (sz == 4) + return FU_VERSION_FORMAT_QUAD; + + /* unknown! */ + return FU_VERSION_FORMAT_UNKNOWN; +} + +/** + * fu_common_vercmp: + * @version_a: the release version, e.g. 1.2.3 + * @version_b: the release version, e.g. 1.2.3.1 + * + * Compares version numbers for sorting. + * + * Returns: -1 if a < b, +1 if a > b, 0 if they are equal, and %G_MAXINT on error + * + * Since: 0.3.5 + */ +gint +fu_common_vercmp (const gchar *version_a, const gchar *version_b) +{ + guint longest_split; + g_autofree gchar *str_a = NULL; + g_autofree gchar *str_b = NULL; + g_auto(GStrv) split_a = NULL; + g_auto(GStrv) split_b = NULL; + + /* sanity check */ + if (version_a == NULL || version_b == NULL) + return G_MAXINT; + + /* optimisation */ + if (g_strcmp0 (version_a, version_b) == 0) + return 0; + + /* split into sections, and try to parse */ + str_a = fu_common_version_parse (version_a); + str_b = fu_common_version_parse (version_b); + split_a = g_strsplit (str_a, ".", -1); + split_b = g_strsplit (str_b, ".", -1); + longest_split = MAX (g_strv_length (split_a), g_strv_length (split_b)); + for (guint i = 0; i < longest_split; i++) { + gchar *endptr_a = NULL; + gchar *endptr_b = NULL; + gint64 ver_a; + gint64 ver_b; + + /* we lost or gained a dot */ + if (split_a[i] == NULL) + return -1; + if (split_b[i] == NULL) + return 1; + + /* compare integers */ + ver_a = g_ascii_strtoll (split_a[i], &endptr_a, 10); + ver_b = g_ascii_strtoll (split_b[i], &endptr_b, 10); + if (ver_a < ver_b) + return -1; + if (ver_a > ver_b) + return 1; + + /* compare strings */ + if ((endptr_a != NULL && endptr_a[0] != '\0') || + (endptr_b != NULL && endptr_b[0] != '\0')) { + gint rc = fu_common_vercmp_chunk (endptr_a, endptr_b); + if (rc < 0) + return -1; + if (rc > 0) + return 1; + } + } + + /* we really shouldn't get here */ + return 0; +} diff --git a/src/fu-common-version.h b/src/fu-common-version.h new file mode 100644 index 000000000..3538316b9 --- /dev/null +++ b/src/fu-common-version.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_COMMON_VERSION_H__ +#define __FU_COMMON_VERSION_H__ + +#include + +/** + * FuVersionFormat: + * @FU_VERSION_FORMAT_UNKNOWN: Unknown version format + * @FU_VERSION_FORMAT_PLAIN: Use plain integer version numbers + * @FU_VERSION_FORMAT_QUAD: Use Dell-style AA.BB.CC.DD version numbers + * @FU_VERSION_FORMAT_TRIPLET: Use Microsoft-style AA.BB.CCDD version numbers + * @FU_VERSION_FORMAT_PAIR: Use two AABB.CCDD version numbers + * @FU_VERSION_FORMAT_BCD: Use binary coded decimal notation + * @FU_VERSION_FORMAT_INTEL_ME: Use Intel ME-style bitshifted notation + * @FU_VERSION_FORMAT_INTEL_ME2: Use Intel ME-style A.B.CC.DDDD notation notation + * + * The flags used when parsing version numbers. + **/ +typedef enum { + FU_VERSION_FORMAT_UNKNOWN, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_PLAIN, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_QUAD, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_TRIPLET, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_PAIR, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_BCD, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_INTEL_ME, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_INTEL_ME2, /* Since: 1.2.0 */ + /*< private >*/ + FU_VERSION_FORMAT_LAST +} FuVersionFormat; + +FuVersionFormat fu_common_version_format_from_string (const gchar *str); +const gchar *fu_common_version_format_to_string (FuVersionFormat kind); + +gint fu_common_vercmp (const gchar *version_a, + const gchar *version_b); +gchar *fu_common_version_from_uint32 (guint32 val, + FuVersionFormat flags); +gchar *fu_common_version_from_uint16 (guint16 val, + FuVersionFormat flags); +gchar *fu_common_version_parse (const gchar *version); +FuVersionFormat fu_common_version_guess_format (const gchar *version); + +#endif /* __FU_COMMON_VERSION_H__ */ diff --git a/src/fu-common.c b/src/fu-common.c index 51360d3d2..893252edc 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -928,6 +928,12 @@ fu_common_get_path (FuPathKind path_kind) if (tmp != NULL) return g_strdup (tmp); return g_strdup ("/sys/firmware"); + /* /sys/firmware */ + case FU_PATH_KIND_SYSFSDIR_TPM: + tmp = g_getenv ("FWUPD_SYSFSTPMDIR"); + if (tmp != NULL) + return g_strdup (tmp); + return g_strdup ("/sys/class/tpm"); /* /sys/bus/platform/drivers */ case FU_PATH_KIND_SYSFSDIR_DRIVERS: tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR"); @@ -993,3 +999,217 @@ fu_common_get_path (FuPathKind path_kind) return NULL; } + +/** + * fu_common_string_replace: + * @string: The #GString to operate on + * @search: The text to search for + * @replace: The text to use for substitutions + * + * Performs multiple search and replace operations on the given string. + * + * Returns: the number of replacements done, or 0 if @search is not found. + * + * Since: 1.2.0 + **/ +guint +fu_common_string_replace (GString *string, const gchar *search, const gchar *replace) +{ + gchar *tmp; + guint count = 0; + gsize search_idx = 0; + gsize replace_len; + gsize search_len; + + g_return_val_if_fail (string != NULL, 0); + g_return_val_if_fail (search != NULL, 0); + g_return_val_if_fail (replace != NULL, 0); + + /* nothing to do */ + if (string->len == 0) + return 0; + + search_len = strlen (search); + replace_len = strlen (replace); + + do { + tmp = g_strstr_len (string->str + search_idx, -1, search); + if (tmp == NULL) + break; + + /* advance the counter in case @replace contains @search */ + search_idx = (gsize) (tmp - string->str); + + /* reallocate the string if required */ + if (search_len > replace_len) { + g_string_erase (string, + (gssize) search_idx, + (gssize) (search_len - replace_len)); + memcpy (tmp, replace, replace_len); + } else if (search_len < replace_len) { + g_string_insert_len (string, + (gssize) search_idx, + replace, + (gssize) (replace_len - search_len)); + /* we have to treat this specially as it could have + * been reallocated when the insertion happened */ + memcpy (string->str + search_idx, replace, replace_len); + } else { + /* just memcmp in the new string */ + memcpy (tmp, replace, replace_len); + } + search_idx += replace_len; + count++; + } while (TRUE); + + return count; +} + +/** + * fu_common_dump_full: + * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL + * @title: prefix title, or %NULL + * @data: buffer to print + * @len: the size of @data + * @columns: break new lines after this many bytes + * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII + * + * Dumps a raw buffer to the screen. + * + * Since: 1.2.4 + **/ +void +fu_common_dump_full (const gchar *log_domain, + const gchar *title, + const guint8 *data, + gsize len, + guint columns, + FuDumpFlags flags) +{ + g_autoptr(GString) str = g_string_new (NULL); + + /* optional */ + if (title != NULL) + g_string_append_printf (str, "%s:", title); + + /* if more than can fit on one line then start afresh */ + if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) { + g_string_append (str, "\n"); + } else { + for (gsize i = str->len; i < 16; i++) + g_string_append (str, " "); + } + + /* offset line */ + if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) { + g_string_append (str, " │ "); + for (gsize i = 0; i < columns; i++) + g_string_append_printf (str, "%02x ", (guint) i); + g_string_append (str, "\n───────┼"); + for (gsize i = 0; i < columns; i++) + g_string_append (str, "───"); + g_string_append_printf (str, "\n0x%04x │ ", (guint) 0); + } + + /* print each row */ + for (gsize i = 0; i < len; i++) { + g_string_append_printf (str, "%02x ", data[i]); + + /* optionally print ASCII char */ + if (flags & FU_DUMP_FLAGS_SHOW_ASCII) { + if (g_ascii_isprint (data[i])) + g_string_append_printf (str, "[%c] ", data[i]); + else + g_string_append (str, "[?] "); + } + + /* new row required */ + if (i > 0 && i != len - 1 && (i + 1) % columns == 0) { + g_string_append (str, "\n"); + if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) + g_string_append_printf (str, "0x%04x │ ", (guint) i + 1); + } + } + g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str); +} + +/** + * fu_common_dump_raw: + * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL + * @title: prefix title, or %NULL + * @data: buffer to print + * @len: the size of @data + * + * Dumps a raw buffer to the screen. + * + * Since: 1.2.2 + **/ +void +fu_common_dump_raw (const gchar *log_domain, + const gchar *title, + const guint8 *data, + gsize len) +{ + FuDumpFlags flags = FU_DUMP_FLAGS_NONE; + if (len > 64) + flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES; + fu_common_dump_full (log_domain, title, data, len, 32, flags); +} + +/** + * fu_common_dump_raw: + * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL + * @title: prefix title, or %NULL + * @bytes: a #GBytes + * + * Dumps a byte buffer to the screen. + * + * Since: 1.2.2 + **/ +void +fu_common_dump_bytes (const gchar *log_domain, + const gchar *title, + GBytes *bytes) +{ + gsize len = 0; + const guint8 *data = g_bytes_get_data (bytes, &len); + fu_common_dump_raw (log_domain, title, data, len); +} + +/** + * fu_common_bytes_align: + * @bytes: a #GBytes + * @blksz: block size in bytes + * @padval: the byte used to pad the byte buffer + * + * Aligns a block of memory to @blksize using the @padval value; if + * the block is already aligned then the original @bytes is returned. + * + * Returns: (transfer full): a #GBytes, possibly @bytes + * + * Since: 1.2.4 + **/ +GBytes * +fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval) +{ + const guint8 *data; + gsize sz; + + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (blksz > 0, NULL); + + /* pad */ + data = g_bytes_get_data (bytes, &sz); + if (sz % blksz != 0) { + gsize sz_align = ((sz / blksz) + 1) * blksz; + guint8 *data_align = g_malloc (sz_align); + memcpy (data_align, data, sz); + memset (data_align + sz, padval, sz_align - sz); + g_debug ("aligning 0x%x bytes to 0x%x", + (guint) sz, (guint) sz_align); + return g_bytes_new_take (data_align, sz_align); + } + + /* perfectly aligned */ + return g_bytes_ref (bytes); +} diff --git a/src/fu-common.h b/src/fu-common.h index f605cc649..9c7252195 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -15,6 +15,13 @@ typedef enum { FU_APP_FLAGS_LAST } FuAppFlags; +typedef enum { + FU_DUMP_FLAGS_NONE = 0, + FU_DUMP_FLAGS_SHOW_ASCII = 1 << 0, + FU_DUMP_FLAGS_SHOW_ADDRESSES = 1 << 1, + FU_DUMP_FLAGS_LAST +} FuDumpFlags; + typedef enum { FU_PATH_KIND_CACHEDIR_PKG, FU_PATH_KIND_DATADIR_PKG, @@ -26,6 +33,7 @@ typedef enum { FU_PATH_KIND_SYSCONFDIR_PKG, FU_PATH_KIND_SYSFSDIR_FW, FU_PATH_KIND_SYSFSDIR_DRIVERS, + FU_PATH_KIND_SYSFSDIR_TPM, FU_PATH_KIND_LAST } FuPathKind; @@ -65,6 +73,22 @@ guint64 fu_common_strtoull (const gchar *str); gchar *fu_common_find_program_in_path (const gchar *basename, GError **error); gchar *fu_common_strstrip (const gchar *str); +void fu_common_dump_raw (const gchar *log_domain, + const gchar *title, + const guint8 *data, + gsize len); +void fu_common_dump_full (const gchar *log_domain, + const gchar *title, + const guint8 *data, + gsize len, + guint columns, + FuDumpFlags flags); +void fu_common_dump_bytes (const gchar *log_domain, + const gchar *title, + GBytes *bytes); +GBytes *fu_common_bytes_align (GBytes *bytes, + gsize blksz, + gchar padval); typedef guint FuEndianType; @@ -79,4 +103,8 @@ guint16 fu_common_read_uint16 (const guint8 *buf, guint32 fu_common_read_uint32 (const guint8 *buf, FuEndianType endian); +guint fu_common_string_replace (GString *string, + const gchar *search, + const gchar *replace); + #endif /* __FU_COMMON_H__ */ diff --git a/src/fu-config.c b/src/fu-config.c index c7aeba5b0..27eccfc42 100644 --- a/src/fu-config.c +++ b/src/fu-config.c @@ -8,7 +8,7 @@ #include "config.h" -#include +#include #include #include #include @@ -31,7 +31,8 @@ struct _FuConfig GPtrArray *blacklist_devices; GPtrArray *blacklist_plugins; guint64 archive_size_max; - AsStore *store_remotes; + guint idle_timeout; + XbSilo *silo; GHashTable *os_release; }; @@ -132,70 +133,26 @@ fu_config_get_remote_agreement_default (FwupdRemote *self, GError **error) } static GString * -fu_config_get_remote_agreement_for_app (FwupdRemote *self, AsApp *app, GError **error) +fu_config_get_remote_agreement_for_app (FwupdRemote *self, XbNode *component, GError **error) { -#if AS_CHECK_VERSION(0,7,8) - const gchar *tmp = NULL; - AsAgreement *agreement; - AsAgreementSection *section; - - /* get the default agreement section */ - agreement = as_app_get_agreement_default (app); - if (agreement == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No agreement found"); - return NULL; - } - section = as_agreement_get_section_default (agreement); - if (section == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No default section for agreement found"); - return NULL; - } - tmp = as_agreement_section_get_description (section, NULL); - if (tmp == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No description found in agreement section"); - return NULL; - } - return g_string_new (tmp); -#else - AsFormat *format; - GNode *n; - g_autoptr(AsNode) root = NULL; - g_autoptr(GFile) file = NULL; - - /* parse the XML file */ - format = as_app_get_format_default (app); - if (format == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No format for Metainfo file"); - return NULL; - } - file = g_file_new_for_path (as_format_get_filename (format)); - root = as_node_from_file (file, AS_NODE_FROM_XML_FLAG_NONE, NULL, error); - if (root == NULL) - return NULL; + XbNode *n; + g_autofree gchar *tmp = NULL; + g_autoptr(GError) error_local = NULL; /* manually find the first agreement section */ - n = as_node_find (root, "component/agreement/agreement_section/description"); + n = xb_node_query_first (component, "agreement/agreement_section/description/*", &error_local); if (n == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No agreement description found"); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No agreement description found: %s", + error_local->message); return NULL; } - return as_node_to_xml (n->children, AS_NODE_TO_XML_FLAG_INCLUDE_SIBLINGS); -#endif + tmp = xb_node_export (n, XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS, error); + if (tmp == NULL) + return NULL; + return g_string_new (tmp); } static gchar * @@ -213,8 +170,10 @@ fu_config_add_remotes_for_path (FuConfig *self, const gchar *path, GError **erro g_autoptr(GDir) dir = NULL; path_remotes = g_build_filename (path, "remotes.d", NULL); - if (!g_file_test (path_remotes, G_FILE_TEST_EXISTS)) + if (!g_file_test (path_remotes, G_FILE_TEST_EXISTS)) { + g_debug ("path %s does not exist", path_remotes); return TRUE; + } if (!fu_config_add_inotify (self, path_remotes, error)) return FALSE; dir = g_dir_open (path_remotes, 0, error); @@ -248,9 +207,13 @@ fu_config_add_remotes_for_path (FuConfig *self, const gchar *path, GError **erro if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DOWNLOAD) { g_autoptr(GString) agreement_markup = NULL; g_autofree gchar *component_id = fu_config_build_remote_component_id (remote); - AsApp *app = as_store_get_app_by_id (self->store_remotes, component_id); - if (app != NULL) { - agreement_markup = fu_config_get_remote_agreement_for_app (remote, app, error); + g_autoptr(XbNode) component = NULL; + g_autofree gchar *xpath = NULL; + + xpath = g_strdup_printf ("component/id[text()='%s']/..", component_id); + component = xb_silo_query_first (self->silo, xpath, NULL); + if (component != NULL) { + agreement_markup = fu_config_get_remote_agreement_for_app (remote, component, error); } else { agreement_markup = fu_config_get_remote_agreement_default (remote, error); } @@ -261,11 +224,11 @@ fu_config_add_remotes_for_path (FuConfig *self, const gchar *path, GError **erro tmp = g_hash_table_lookup (self->os_release, "NAME"); if (tmp == NULL) tmp = "this distribution"; - as_utils_string_replace (agreement_markup, "$OS_RELEASE:NAME$", tmp); + fu_common_string_replace (agreement_markup, "$OS_RELEASE:NAME$", tmp); tmp = g_hash_table_lookup (self->os_release, "BUG_REPORT_URL"); if (tmp == NULL) tmp = "https://github.com/hughsie/fwupd/issues"; - as_utils_string_replace (agreement_markup, "$OS_RELEASE:BUG_REPORT_URL$", tmp); + fu_common_string_replace (agreement_markup, "$OS_RELEASE:BUG_REPORT_URL$", tmp); fwupd_remote_set_agreement (remote, agreement_markup->str); } @@ -393,6 +356,7 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, { GFileMonitor *monitor; guint64 archive_size_max; + guint idle_timeout; g_auto(GStrv) devices = NULL; g_auto(GStrv) plugins = NULL; g_autoptr(GFile) file = NULL; @@ -450,16 +414,60 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, NULL); if (archive_size_max > 0) self->archive_size_max = archive_size_max *= 0x100000; + + /* get idle timeout */ + idle_timeout = g_key_file_get_uint64 (self->keyfile, + "fwupd", + "IdleTimeout", + NULL); + if (idle_timeout > 0) + self->idle_timeout = idle_timeout; + return TRUE; +} + +static gboolean +fu_config_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_common_get_path (FU_PATH_KIND_DATADIR_PKG); + metainfo_path = g_build_filename (datadir, "metainfo", NULL); + if (!g_file_test (metainfo_path, G_FILE_TEST_EXISTS)) + return TRUE; + + g_debug ("loading %s", metainfo_path); + dir = g_dir_open (metainfo_path, 0, error); + if (dir == NULL) + return FALSE; + while ((fn = g_dir_read_name (dir)) != NULL) { + if (g_str_has_suffix (fn, ".metainfo.xml")) { + g_autofree gchar *filename = g_build_filename (metainfo_path, fn, NULL); + g_autoptr(GFile) file = g_file_new_for_path (filename); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + if (!xb_builder_source_load_file (source, file, + XB_BUILDER_SOURCE_FLAG_NONE, + NULL, error)) + return FALSE; + xb_builder_import_source (builder, source); + } + } return TRUE; } gboolean fu_config_load (FuConfig *self, GError **error) { - g_autofree gchar *datadir = NULL; + const gchar *const *locales = g_get_language_names (); g_autofree gchar *configdir = NULL; - g_autofree gchar *metainfo_path = NULL; g_autofree gchar *config_file = NULL; + g_autofree gchar *cachedirpkg = NULL; + g_autofree gchar *xmlbfn = NULL; + g_autoptr(GFile) xmlb = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); g_return_val_if_fail (FU_IS_CONFIG (self), FALSE); @@ -477,16 +485,23 @@ fu_config_load (FuConfig *self, GError **error) self->os_release = fwupd_get_os_release (error); if (self->os_release == NULL) return FALSE; - as_store_add_filter (self->store_remotes, AS_APP_KIND_SOURCE); - datadir = fu_common_get_path (FU_PATH_KIND_DATADIR_PKG); - metainfo_path = g_build_filename (datadir, "metainfo", NULL); - if (g_file_test (metainfo_path, G_FILE_TEST_EXISTS)) { - if (!as_store_load_path (self->store_remotes, - metainfo_path, - NULL, /* cancellable */ - error)) - return FALSE; - } + if (!fu_config_load_metainfos (builder, error)) + return FALSE; + + /* add the locales, which is really only going to be 'C' or 'en' */ + for (guint i = 0; locales[i] != NULL; i++) + xb_builder_add_locale (builder, locales[i]); + + /* build the metainfo silo */ + cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG); + xmlbfn = g_build_filename (cachedirpkg, "metainfo.xmlb", NULL); + xmlb = g_file_new_for_path (xmlbfn); + self->silo = xb_builder_ensure (builder, xmlb, + XB_BUILDER_COMPILE_FLAG_SINGLE_LANG | + XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID, + NULL, error); + if (self->silo == NULL) + return FALSE; /* load remotes */ if (!fu_config_load_remotes (self, error)) @@ -503,6 +518,13 @@ fu_config_get_remotes (FuConfig *self) return self->remotes; } +guint +fu_config_get_idle_timeout (FuConfig *self) +{ + g_return_val_if_fail (FU_IS_CONFIG (self), 0); + return self->idle_timeout; +} + FwupdRemote * fu_config_get_remote_by_id (FuConfig *self, const gchar *remote_id) { @@ -547,7 +569,6 @@ fu_config_init (FuConfig *self) self->blacklist_plugins = g_ptr_array_new_with_free_func (g_free); self->remotes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - self->store_remotes = as_store_new (); } static void @@ -557,12 +578,13 @@ fu_config_finalize (GObject *obj) if (self->os_release != NULL) g_hash_table_unref (self->os_release); + if (self->silo != NULL) + g_object_unref (self->silo); g_key_file_unref (self->keyfile); g_ptr_array_unref (self->blacklist_devices); g_ptr_array_unref (self->blacklist_plugins); g_ptr_array_unref (self->remotes); g_ptr_array_unref (self->monitors); - g_object_unref (self->store_remotes); G_OBJECT_CLASS (fu_config_parent_class)->finalize (obj); } diff --git a/src/fu-config.h b/src/fu-config.h index 8602f883f..a429cf32b 100644 --- a/src/fu-config.h +++ b/src/fu-config.h @@ -21,6 +21,7 @@ gboolean fu_config_load (FuConfig *self, GError **error); guint64 fu_config_get_archive_size_max (FuConfig *self); +guint fu_config_get_idle_timeout (FuConfig *self); GPtrArray *fu_config_get_blacklist_devices (FuConfig *self); GPtrArray *fu_config_get_blacklist_plugins (FuConfig *self); GPtrArray *fu_config_get_remotes (FuConfig *self); diff --git a/src/fu-debug.c b/src/fu-debug.c index 0384e2479..5497e469a 100644 --- a/src/fu-debug.c +++ b/src/fu-debug.c @@ -14,6 +14,7 @@ #include typedef struct { + GOptionGroup *group; gboolean verbose; gboolean console; gchar **plugin_verbose; @@ -23,6 +24,8 @@ typedef struct { static void fu_debug_free (FuDebug *self) { + g_option_group_set_parse_hooks (self->group, NULL, NULL); + g_option_group_unref (self->group); g_strfreev (self->plugin_verbose); g_strfreev (self->daemon_verbose); g_free (self); @@ -174,19 +177,19 @@ fu_debug_post_parse_hook (GOptionContext *context, return TRUE; } +/*(transfer): full */ GOptionGroup * fu_debug_get_option_group (void) { - GOptionGroup *group; FuDebug *self = g_new0 (FuDebug, 1); - group = g_option_group_new ("debug", - /* TRANSLATORS: for the --verbose arg */ - _("Debugging Options"), - /* TRANSLATORS: for the --verbose arg */ - _("Show debugging options"), - self, (GDestroyNotify) fu_debug_free); - g_option_group_set_parse_hooks (group, + self->group = g_option_group_new ("debug", + /* TRANSLATORS: for the --verbose arg */ + _("Debugging Options"), + /* TRANSLATORS: for the --verbose arg */ + _("Show debugging options"), + self, (GDestroyNotify) fu_debug_free); + g_option_group_set_parse_hooks (self->group, fu_debug_pre_parse_hook, fu_debug_post_parse_hook); - return group; + return g_option_group_ref (self->group); } diff --git a/src/fu-device-private.h b/src/fu-device-private.h index 5eb5bf897..e4d30ca99 100644 --- a/src/fu-device-private.h +++ b/src/fu-device-private.h @@ -8,6 +8,7 @@ #define __FU_DEVICE_PRIVATE_H #include +#include G_BEGIN_DECLS @@ -26,6 +27,8 @@ void fu_device_set_alternate (FuDevice *self, FuDevice *alternate); gboolean fu_device_ensure_id (FuDevice *self, GError **error); +void fu_device_incorporate_from_component (FuDevice *device, + XbNode *component); G_END_DECLS diff --git a/src/fu-device.c b/src/fu-device.c index 1be6c0c35..e8c78c206 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -9,11 +9,12 @@ #include "config.h" #include -#include #include #include #include "fu-common.h" +#include "fu-common-guid.h" +#include "fu-common-version.h" #include "fu-device-private.h" #include "fu-mutex.h" @@ -43,6 +44,7 @@ typedef struct { GPtrArray *children; guint remove_delay; /* ms */ FwupdStatus status; + FuVersionFormat version_format; guint progress; guint order; guint priority; @@ -535,8 +537,8 @@ fu_device_add_parent_guid (FuDevice *self, const gchar *guid) g_return_if_fail (guid != NULL); /* make valid */ - if (!as_utils_guid_is_valid (guid)) { - g_autofree gchar *tmp = as_utils_guid_from_string (guid); + if (!fu_common_guid_is_valid (guid)) { + g_autofree gchar *tmp = fu_common_guid_from_string (guid); if (fu_device_has_parent_guid (self, tmp)) return; g_debug ("using %s for %s", tmp, guid); @@ -676,6 +678,10 @@ fu_device_set_quirk_kv (FuDevice *self, fu_device_set_install_duration (self, fu_common_strtoull (value)); return TRUE; } + if (g_strcmp0 (key, FU_QUIRKS_VERSION_FORMAT) == 0) { + fu_device_set_version_format (self, fu_common_version_format_from_string (value)); + return TRUE; + } if (g_strcmp0 (key, FU_QUIRKS_CHILDREN) == 0) { g_auto(GStrv) sections = g_strsplit (value, ",", -1); for (guint i = 0; sections[i] != NULL; i++) { @@ -764,13 +770,37 @@ fu_device_add_guid_safe (FuDevice *self, const gchar *guid) fu_device_add_guid_quirks (self, guid); } +/** + * fu_device_has_guid: + * @self: A #FuDevice + * @guid: A GUID, e.g. `WacomAES` + * + * Finds out if the device has a specific GUID. + * + * Returns: %TRUE if the GUID is found + * + * Since: 1.2.2 + **/ +gboolean +fu_device_has_guid (FuDevice *self, const gchar *guid) +{ + /* make valid */ + if (!fu_common_guid_is_valid (guid)) { + g_autofree gchar *tmp = fu_common_guid_from_string (guid); + return fwupd_device_has_guid (FWUPD_DEVICE (self), tmp); + } + + /* already valid */ + return fwupd_device_has_guid (FWUPD_DEVICE (self), guid); +} + /** * fu_device_add_guid: * @self: A #FuDevice * @guid: A GUID, e.g. `2082b5e0-7a64-478a-b1b2-e3404fab6dad` * * Adds a GUID to the device. If the @guid argument is not a valid GUID then it - * is converted to a GUID using as_utils_guid_from_string(). + * is converted to a GUID using fu_common_guid_from_string(). * * Since: 0.7.2 **/ @@ -778,8 +808,8 @@ void fu_device_add_guid (FuDevice *self, const gchar *guid) { /* make valid */ - if (!as_utils_guid_is_valid (guid)) { - g_autofree gchar *tmp = as_utils_guid_from_string (guid); + if (!fu_common_guid_is_valid (guid)) { + g_autofree gchar *tmp = fu_common_guid_from_string (guid); g_debug ("using %s for %s", tmp, guid); fu_device_add_guid_safe (self, tmp); return; @@ -795,7 +825,7 @@ fu_device_add_guid (FuDevice *self, const gchar *guid) * @guid: A GUID, e.g. `2082b5e0-7a64-478a-b1b2-e3404fab6dad` * * Adds a GUID to the device. If the @guid argument is not a valid GUID then it - * is converted to a GUID using as_utils_guid_from_string(). + * is converted to a GUID using fu_common_guid_from_string(). * * A counterpart GUID is typically the GUID of the same device in bootloader * or runtime mode, if they have a different device PCI or USB ID. Adding this @@ -807,8 +837,8 @@ void fu_device_add_counterpart_guid (FuDevice *self, const gchar *guid) { /* make valid */ - if (!as_utils_guid_is_valid (guid)) { - g_autofree gchar *tmp = as_utils_guid_from_string (guid); + if (!fu_common_guid_is_valid (guid)) { + g_autofree gchar *tmp = fu_common_guid_from_string (guid); g_debug ("using %s for counterpart %s", tmp, guid); fwupd_device_add_guid (FWUPD_DEVICE (self), tmp); return; @@ -1011,7 +1041,7 @@ fu_device_set_name (FuDevice *self, const gchar *value) } g_strdelimit (new->str, "_", ' '); - as_utils_string_replace (new, "(TM)", "™"); + fu_common_string_replace (new, "(TM)", "™"); fwupd_device_set_name (FWUPD_DEVICE (self), new->str); } @@ -1041,6 +1071,56 @@ fu_device_set_id (FuDevice *self, const gchar *id) fwupd_device_set_id (FWUPD_DEVICE (self), id_hash); } +static gboolean +fu_device_is_valid_semver_char (gchar c) +{ + if (g_ascii_isdigit (c)) + return TRUE; + if (c == '.') + return TRUE; + return FALSE; +} + +/** + * fu_device_set_version: + * @self: A #FuDevice + * @version: a string, e.g. `1.2.3` + * + * Sets the device version, autodetecting the version format if required. + * + * Since: 1.2.1 + **/ +void +fu_device_set_version (FuDevice *self, const gchar *version) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GString) version_safe = NULL; + + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (version != NULL); + + /* sanitize if required */ + if (priv->version_format != FU_VERSION_FORMAT_UNKNOWN && + priv->version_format != FU_VERSION_FORMAT_PLAIN) { + version_safe = g_string_new (NULL); + for (guint i = 0; version[i] != '\0'; i++) { + if (fu_device_is_valid_semver_char (version[i])) + g_string_append_c (version_safe, version[i]); + } + if (g_strcmp0 (version, version_safe->str) != 0) { + g_debug ("converted '%s' to '%s'", + version, version_safe->str); + } + } else { + version_safe = g_string_new (version); + } + + /* try to autodetect the version-format */ + if (priv->version_format == FU_VERSION_FORMAT_UNKNOWN) + priv->version_format = fu_common_version_guess_format (version_safe->str); + fwupd_device_set_version (FWUPD_DEVICE (self), version_safe->str); +} + /** * fu_device_ensure_id: * @self: A #FuDevice @@ -1351,6 +1431,43 @@ fu_device_set_status (FuDevice *self, FwupdStatus status) g_object_notify (G_OBJECT (self), "status"); } +/** + * fu_device_get_version_format: + * @self: A #FuDevice + * + * Returns how the device version should be formatted. + * + * Returns: the version format, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Since: 1.2.0 + **/ +FuVersionFormat +fu_device_get_version_format (FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); + return priv->version_format; +} + +/** + * fu_device_set_version_format: + * @self: A #FuDevice + * @version_format: the version_format value, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Sets how the version should be formatted. + * + * Since: 1.2.0 + **/ +void +fu_device_set_version_format (FuDevice *self, FuVersionFormat version_format) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); + if (priv->version_format == version_format) + return; + priv->version_format = version_format; +} + /** * fu_device_get_progress: * @self: A #FuDevice @@ -1435,6 +1552,10 @@ fu_device_to_string (FuDevice *self) tmp = fwupd_device_to_string (FWUPD_DEVICE (self)); if (tmp != NULL && tmp[0] != '\0') g_string_append (str, tmp); + if (priv->version_format != FU_VERSION_FORMAT_UNKNOWN) { + fwupd_pad_kv_str (str, "VersionFormat", + fu_common_version_format_to_string (priv->version_format)); + } if (priv->alternate_id != NULL) fwupd_pad_kv_str (str, "AlternateId", priv->alternate_id); if (priv->equivalent_id != NULL) @@ -1968,6 +2089,24 @@ fu_device_incorporate (FuDevice *self, FuDevice *donor) klass->incorporate (self, donor); } +/** + * fu_device_incorporate_from_component: + * @self: A #FuDevice + * @component: A #XbNode + * + * Copy all properties from the donor AppStream component. + * + * Since: 1.2.4 + **/ +void +fu_device_incorporate_from_component (FuDevice *device, XbNode *component) +{ + const gchar *tmp; + tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateMessage']", NULL); + if (tmp != NULL) + fwupd_device_set_update_message (FWUPD_DEVICE (device), tmp); +} + static void fu_device_class_init (FuDeviceClass *klass) { diff --git a/src/fu-device.h b/src/fu-device.h index 3f421d41a..27461f91a 100644 --- a/src/fu-device.h +++ b/src/fu-device.h @@ -11,6 +11,7 @@ #include #include "fu-quirks.h" +#include "fu-common-version.h" G_BEGIN_DECLS @@ -84,7 +85,6 @@ FuDevice *fu_device_new (void); #define fu_device_set_created(d,v) fwupd_device_set_created(FWUPD_DEVICE(d),v) #define fu_device_set_description(d,v) fwupd_device_set_description(FWUPD_DEVICE(d),v) #define fu_device_set_flags(d,v) fwupd_device_set_flags(FWUPD_DEVICE(d),v) -#define fu_device_has_guid(d,v) fwupd_device_has_guid(FWUPD_DEVICE(d),v) #define fu_device_set_modified(d,v) fwupd_device_set_modified(FWUPD_DEVICE(d),v) #define fu_device_set_plugin(d,v) fwupd_device_set_plugin(FWUPD_DEVICE(d),v) #define fu_device_set_serial(d,v) fwupd_device_set_serial(FWUPD_DEVICE(d),v) @@ -93,7 +93,6 @@ FuDevice *fu_device_new (void); #define fu_device_set_update_state(d,v) fwupd_device_set_update_state(FWUPD_DEVICE(d),v) #define fu_device_set_vendor(d,v) fwupd_device_set_vendor(FWUPD_DEVICE(d),v) #define fu_device_set_vendor_id(d,v) fwupd_device_set_vendor_id(FWUPD_DEVICE(d),v) -#define fu_device_set_version(d,v) fwupd_device_set_version(FWUPD_DEVICE(d),v) #define fu_device_set_version_lowest(d,v) fwupd_device_set_version_lowest(FWUPD_DEVICE(d),v) #define fu_device_set_version_bootloader(d,v) fwupd_device_set_version_bootloader(FWUPD_DEVICE(d),v) #define fu_device_set_flashes_left(d,v) fwupd_device_set_flashes_left(FWUPD_DEVICE(d),v) @@ -130,6 +129,8 @@ void fu_device_set_equivalent_id (FuDevice *self, const gchar *equivalent_id); void fu_device_add_guid (FuDevice *self, const gchar *guid); +gboolean fu_device_has_guid (FuDevice *self, + const gchar *guid); gchar *fu_device_get_guids_as_str (FuDevice *self); FuDevice *fu_device_get_alternate (FuDevice *self); FuDevice *fu_device_get_parent (FuDevice *self); @@ -157,6 +158,8 @@ void fu_device_set_metadata_integer (FuDevice *self, guint value); void fu_device_set_id (FuDevice *self, const gchar *id); +void fu_device_set_version (FuDevice *self, + const gchar *version); const gchar *fu_device_get_physical_id (FuDevice *self); void fu_device_set_physical_id (FuDevice *self, const gchar *physical_id); @@ -176,6 +179,9 @@ void fu_device_set_remove_delay (FuDevice *self, FwupdStatus fu_device_get_status (FuDevice *self); void fu_device_set_status (FuDevice *self, FwupdStatus status); +FuVersionFormat fu_device_get_version_format (FuDevice *self); +void fu_device_set_version_format (FuDevice *self, + FuVersionFormat version_format); void fu_device_set_firmware_size_min (FuDevice *self, guint64 size_min); void fu_device_set_firmware_size_max (FuDevice *self, diff --git a/src/fu-engine.c b/src/fu-engine.c index ac102cfa4..d99054f56 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -8,7 +8,6 @@ #include "config.h" -#include #include #include #include @@ -25,6 +24,7 @@ #include "fwupd-resources.h" #include "fu-common-cab.h" +#include "fu-common-guid.h" #include "fu-common.h" #include "fu-config.h" #include "fu-debug.h" @@ -32,7 +32,9 @@ #include "fu-device-private.h" #include "fu-engine.h" #include "fu-hwids.h" +#include "fu-idle.h" #include "fu-keyring-utils.h" +#include "fu-hash.h" #include "fu-history.h" #include "fu-mutex.h" #include "fu-plugin.h" @@ -54,21 +56,23 @@ struct _FuEngine FuConfig *config; FuDeviceList *device_list; FwupdStatus status; + gboolean tainted; guint percentage; FuHistory *history; - AsStore *store; + FuIdle *idle; + XbSilo *silo; gboolean coldplug_running; guint coldplug_id; guint coldplug_delay; FuPluginList *plugin_list; GPtrArray *plugin_filter; - GPtrArray *supported_guids; GPtrArray *udev_subsystems; FuSmbios *smbios; FuHwids *hwids; FuQuirks *quirks; GHashTable *runtime_versions; GHashTable *compile_versions; + gboolean loaded; }; enum { @@ -89,6 +93,7 @@ static void fu_engine_emit_changed (FuEngine *self) { g_signal_emit (self, signals[SIGNAL_CHANGED], 0); + fu_engine_idle_reset (self); } static void @@ -139,6 +144,8 @@ fu_engine_set_percentage (FuEngine *self, guint percentage) static void fu_engine_progress_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self) { + if (fu_device_get_status (device) == FWUPD_STATUS_UNKNOWN) + return; fu_engine_set_percentage (self, fu_device_get_progress (device)); fu_engine_emit_device_changed (self, device); } @@ -200,53 +207,98 @@ fu_engine_device_changed_cb (FuDeviceList *device_list, FuDevice *device, FuEngi fu_engine_emit_device_changed (self, device); } -static const gchar * -_as_release_get_metadata_item (AsRelease *release, const gchar *key) +/* convert hex and decimal versions to dotted style */ +static gchar * +fu_engine_get_release_version (FuEngine *self, XbNode *component, XbNode *rel) { - GBytes *blob = as_release_get_blob (release, key); - if (blob == NULL) + FuVersionFormat fmt = FU_VERSION_FORMAT_TRIPLET; + const gchar *version; + const gchar *version_format; + guint64 ver_uint32; + + /* get version */ + version = xb_node_get_attr (rel, "version"); + if (version == NULL) return NULL; - return (const gchar *) g_bytes_get_data (blob, NULL); + + /* already dotted notation */ + if (g_strstr_len (version, -1, ".") != NULL) + return g_strdup (version); + + /* specified in metadata */ + version_format = xb_node_query_text (component, + "custom/value[@key='LVFS::VersionFormat']", + NULL); + if (version_format != NULL) + fmt = fu_common_version_format_from_string (version_format); + + /* don't touch my version! */ + if (fmt == FU_VERSION_FORMAT_PLAIN) + return g_strdup (version); + + /* parse as integer */ + ver_uint32 = fu_common_strtoull (version); + if (ver_uint32 == 0 || ver_uint32 > G_MAXUINT32) + return g_strdup (version); + + /* convert to dotted decimal */ + return fu_common_version_from_uint32 ((guint32) ver_uint32, fmt); } static void fu_engine_set_release_from_appstream (FuEngine *self, FwupdRelease *rel, - AsApp *app, - AsRelease *release) + XbNode *component, + XbNode *release) { - AsChecksum *csum; FwupdRemote *remote = NULL; const gchar *tmp; const gchar *remote_id; + guint64 tmp64; + g_autofree gchar *version_rel = NULL; + g_autoptr(XbNode) description = NULL; - /* set from the AsApp */ - fwupd_release_set_appstream_id (rel, as_app_get_id (app)); - fwupd_release_set_homepage (rel, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE)); - fwupd_release_set_license (rel, as_app_get_project_license (app)); - fwupd_release_set_name (rel, as_app_get_name (app, NULL)); - fwupd_release_set_summary (rel, as_app_get_comment (app, NULL)); - fwupd_release_set_vendor (rel, as_app_get_developer_name (app, NULL)); - fwupd_release_set_appstream_id (rel, as_app_get_id (app)); + /* set from the component */ + tmp = xb_node_query_text (component, "id", NULL); + if (tmp != NULL) + fwupd_release_set_appstream_id (rel, tmp); + tmp = xb_node_query_text (component, "url[@type='homepage']", NULL); + if (tmp != NULL) + fwupd_release_set_homepage (rel, tmp); + tmp = xb_node_query_text (component, "project_license", NULL); + if (tmp != NULL) + fwupd_release_set_license (rel, tmp); + tmp = xb_node_query_text (component, "name", NULL); + if (tmp != NULL) + fwupd_release_set_name (rel, tmp); + tmp = xb_node_query_text (component, "summary", NULL); + if (tmp != NULL) + fwupd_release_set_summary (rel, tmp); + tmp = xb_node_query_text (component, "developer_name", NULL); + if (tmp != NULL) + fwupd_release_set_vendor (rel, tmp); + + /* the version is fixed up at runtime */ + version_rel = fu_engine_get_release_version (self, component, release); + if (version_rel != NULL) + fwupd_release_set_version (rel, version_rel); /* find the remote */ - remote_id = _as_release_get_metadata_item (release, "fwupd::RemoteId"); + remote_id = xb_node_query_text (component, "../custom/value[@key='fwupd::RemoteId']", NULL); if (remote_id != NULL) { fwupd_release_set_remote_id (rel, remote_id); remote = fu_config_get_remote_by_id (self->config, remote_id); - if (remote == NULL) { - g_warning ("no remote found for release %s", - as_release_get_version (release)); - } + if (remote == NULL) + g_warning ("no remote found for release %s", version_rel); } - - tmp = as_release_get_version (release); - if (tmp != NULL) - fwupd_release_set_version (rel, tmp); - tmp = as_release_get_description (release, NULL); - if (tmp != NULL) - fwupd_release_set_description (rel, tmp); - tmp = as_release_get_location_default (release); + description = xb_node_query_first (release, "description", NULL); + if (description != NULL) { + g_autofree gchar *xml = NULL; + xml = xb_node_export (description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL); + if (xml != NULL) + fwupd_release_set_description (rel, xml); + } + tmp = xb_node_query_text (release, "location", NULL); if (tmp != NULL) { g_autofree gchar *uri = NULL; if (remote != NULL) @@ -254,43 +306,62 @@ fu_engine_set_release_from_appstream (FuEngine *self, if (uri == NULL) uri = g_strdup (tmp); fwupd_release_set_uri (rel, uri); + } else if (remote != NULL && + fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + g_autofree gchar *uri = NULL; + tmp = xb_node_query_text (component, "../custom/value[@key='fwupd::FilenameCache']", NULL); + if (tmp != NULL) { + uri = g_strdup_printf ("file://%s", tmp); + fwupd_release_set_uri (rel, uri); + } } - csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); - if (csum != NULL) { - tmp = as_checksum_get_filename (csum); - if (tmp != NULL) - fwupd_release_set_filename (rel, tmp); + tmp = xb_node_query_text (release, "checksum[@target='content']", NULL); + if (tmp != NULL) + fwupd_release_set_filename (rel, tmp); + tmp = xb_node_query_text (release, "url[@type='details']", NULL); + if (tmp != NULL) + fwupd_release_set_details_url (rel, tmp); + tmp = xb_node_query_text (release, "url[@type='source']", NULL); + if (tmp != NULL) + fwupd_release_set_source_url (rel, tmp); + tmp = xb_node_query_text (release, "checksum[@target='container']", NULL); + if (tmp != NULL) + fwupd_release_add_checksum (rel, tmp); + tmp64 = xb_node_query_text_as_uint (release, "size[@type='installed']", NULL); + if (tmp64 != G_MAXUINT64) { + fwupd_release_set_size (rel, tmp64); + } else { + GBytes *sz = xb_node_get_data (release, "fwupd::ReleaseSize"); + if (sz != NULL) { + const guint64 *sizeptr = g_bytes_get_data (sz, NULL); + fwupd_release_set_size (rel, *sizeptr); + } } - csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTAINER); - if (csum != NULL) { - tmp = as_checksum_get_value (csum); - if (tmp != NULL) - fwupd_release_add_checksum (rel, tmp); - } - fwupd_release_set_size (rel, as_release_get_size (release, AS_SIZE_KIND_INSTALLED)); + tmp64 = xb_node_get_attr_as_uint (release, "install_duration"); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_install_duration (rel, tmp64); + tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); + if (tmp != NULL) + fwupd_release_set_protocol (rel, tmp); + tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateMessage']", NULL); + if (tmp != NULL) + fwupd_release_set_update_message (rel, tmp); } -/* finds the remote-id for the first firmware in the store that matches this +/* finds the remote-id for the first firmware in the silo that matches this * container checksum */ static const gchar * fu_engine_get_remote_id_for_checksum (FuEngine *self, const gchar *csum) { - GPtrArray *array = as_store_get_apps (self->store); - for (guint i = 0; i < array->len; i++) { - AsApp *app = g_ptr_array_index (array, i); - GPtrArray *releases = as_app_get_releases (app); - for (guint j = 0; j < releases->len; j++) { - AsRelease *release = g_ptr_array_index (releases, j); - AsChecksum *checksum; - checksum = as_release_get_checksum_by_target (release, - AS_CHECKSUM_TARGET_CONTAINER); - if (checksum == NULL) - continue; - if (g_strcmp0 (csum, as_checksum_get_value (checksum)) == 0) - return _as_release_get_metadata_item (release, "fwupd::RemoteId"); - } - } - return NULL; + g_autofree gchar *xpath = NULL; + g_autoptr(XbNode) key = NULL; + xpath = g_strdup_printf ("components/component/releases/release/" + "checksum[@target='container'][text()='%s']/../../" + "../../custom/value[@key='fwupd::RemoteId']", csum); + key = xb_silo_query_first (self->silo, xpath, NULL); + if (key == NULL) + return NULL; + return xb_node_get_text (key); } /** @@ -335,63 +406,6 @@ fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error) return TRUE; } -static AsApp * -fu_engine_verify_update_device_to_app (FuDevice *device) -{ - AsApp *app = NULL; - GPtrArray *checksums; - g_autofree gchar *id = NULL; - g_autoptr(AsFormat) format = NULL; - g_autoptr(AsProvide) prov = NULL; - g_autoptr(AsRelease) rel = NULL; - - /* make a plausible ID */ - id = g_strdup_printf ("%s.firmware", fu_device_get_guid_default (device)); - - /* add app to store */ - app = as_app_new (); - as_app_set_id (app, id); - as_app_set_kind (app, AS_APP_KIND_FIRMWARE); - rel = as_release_new (); - as_release_set_version (rel, fu_device_get_version (device)); - checksums = fu_device_get_checksums (device); - for (guint j = 0; j < checksums->len; j++) { - const gchar *checksum = g_ptr_array_index (checksums, j); - g_autoptr(AsChecksum) csum = as_checksum_new (); - as_checksum_set_kind (csum, fwupd_checksum_guess_kind (checksum)); - as_checksum_set_value (csum, checksum); - as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT); - as_release_add_checksum (rel, csum); - } - as_app_add_release (app, rel); - prov = as_provide_new (); - as_provide_set_kind (prov, AS_PROVIDE_KIND_FIRMWARE_FLASHED); - as_provide_set_value (prov, fu_device_get_guid_default (device)); - as_app_add_provide (app, prov); - format = as_format_new (); - as_format_set_kind (format, AS_FORMAT_KIND_UNKNOWN); - as_app_add_format (app, format); - return app; -} - -static AsStore * -fu_engine_load_verify_store (GError **error) -{ - const gchar *fn = "/var/lib/fwupd/verify.xml"; - g_autoptr(AsStore) store = NULL; - g_autoptr(GFile) file = NULL; - - /* load existing store */ - store = as_store_new (); - as_store_set_api_version (store, 0.9); - file = g_file_new_for_path (fn); - if (g_file_query_exists (file, NULL)) { - if (!as_store_from_file (store, file, NULL, NULL, error)) - return NULL; - } - return g_steal_pointer (&store); -} - /** * fu_engine_modify_remote: * @self: A #FuEngine @@ -400,7 +414,7 @@ fu_engine_load_verify_store (GError **error) * @value: the key, e.g. `true` * @error: A #GError, or %NULL * - * Updates the verification store entry for a specific device. + * Updates the verification silo entry for a specific device. * * Returns: %TRUE for success **/ @@ -507,13 +521,25 @@ fu_engine_modify_device (FuEngine *self, return FALSE; } +static const gchar * +fu_engine_checksum_type_to_string (GChecksumType checksum_type) +{ + if (checksum_type == G_CHECKSUM_SHA1) + return "sha1"; + if (checksum_type == G_CHECKSUM_SHA256) + return "sha256"; + if (checksum_type == G_CHECKSUM_SHA512) + return "sha512"; + return "sha1"; +} + /** * fu_engine_verify_update: * @self: A #FuEngine * @device_id: A device ID * @error: A #GError, or %NULL * - * Updates the verification store entry for a specific device. + * Updates the verification silo entry for a specific device. * * Returns: %TRUE for success **/ @@ -522,11 +548,17 @@ fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error) { FuPlugin *plugin; GPtrArray *checksums; - const gchar *fn = "/var/lib/fwupd/verify.xml"; - g_autoptr(AsApp) app = NULL; - g_autoptr(AsStore) store = NULL; + GPtrArray *guids; + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; g_autoptr(FuDevice) device = NULL; g_autoptr(GFile) file = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderNode) component = NULL; + g_autoptr(XbBuilderNode) provides = NULL; + g_autoptr(XbBuilderNode) release = NULL; + g_autoptr(XbBuilderNode) releases = NULL; + g_autoptr(XbSilo) silo = NULL; g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); g_return_val_if_fail (device_id != NULL, FALSE); @@ -563,38 +595,68 @@ fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error) return FALSE; } - /* load existing store */ - store = fu_engine_load_verify_store (error); - if (store == NULL) + /* build XML */ + component = xb_builder_node_insert (NULL, "component", + "type", "firmware", + NULL); + provides = xb_builder_node_insert (component, "provides", NULL); + guids = fu_device_get_guids (device); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index (guids, i); + g_autoptr(XbBuilderNode) provide = NULL; + provide = xb_builder_node_insert (provides, "firmware", + "type", "flashed", + NULL); + xb_builder_node_set_text (provide, guid, -1); + } + releases = xb_builder_node_insert (component, "releases", NULL); + release = xb_builder_node_insert (releases, "release", + "version", fu_device_get_version (device), + NULL); + for (guint i = 0; i < checksums->len; i++) { + const gchar *checksum = g_ptr_array_index (checksums, i); + GChecksumType kind = fwupd_checksum_guess_kind (checksum); + g_autoptr(XbBuilderNode) csum = NULL; + csum = xb_builder_node_insert (release, "checksum", + "type", fu_engine_checksum_type_to_string (kind), + "target", "content", + NULL); + xb_builder_node_set_text (csum, checksum, -1); + } + xb_builder_import_node (builder, component); + + /* save silo */ + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id); + file = g_file_new_for_path (fn); + silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return FALSE; + if (!xb_silo_export_file (silo, file, + XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE, + NULL, error)) return FALSE; - /* add to store */ - app = fu_engine_verify_update_device_to_app (device); - as_store_remove_app_by_id (store, as_app_get_id (app)); - as_store_add_app (store, app); - - /* write */ - g_debug ("writing %s", fn); - file = g_file_new_for_path (fn); - return as_store_to_file (store, file, - AS_NODE_TO_XML_FLAG_ADD_HEADER | - AS_NODE_TO_XML_FLAG_FORMAT_INDENT | - AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE, - NULL, error); + /* success */ + return TRUE; } -static AsApp * -fu_engine_store_get_app_by_guids (AsStore *store, FuDevice *device) +XbNode * +fu_engine_get_component_by_guids (FuEngine *self, FuDevice *device) { GPtrArray *guids = fu_device_get_guids (device); + g_autoptr(GString) xpath = g_string_new (NULL); + g_autoptr(XbNode) component = NULL; for (guint i = 0; i < guids->len; i++) { - AsApp *app = NULL; - app = as_store_get_app_by_provide (store, - AS_PROVIDE_KIND_FIRMWARE_FLASHED, - g_ptr_array_index (guids, i)); - if (app != NULL) - return app; + const gchar *guid = g_ptr_array_index (guids, i); + xb_string_append_union (xpath, + "components/component/" + "provides/firmware[@type='flashed'][text()='%s']/" + "../..", guid); } + component = xb_silo_query_first (self->silo, xpath->str, NULL); + if (component != NULL) + return g_steal_pointer (&component); return NULL; } @@ -604,22 +666,24 @@ fu_engine_store_get_app_by_guids (AsStore *store, FuDevice *device) * @device_id: A device ID * @error: A #GError, or %NULL * - * Verifies a device firmware checksum using the verification store entry. + * Verifies a device firmware checksum using the verification silo entry. * * Returns: %TRUE for success **/ gboolean fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) { - AsApp *app; - AsChecksum *csum; - AsRelease *release; FuPlugin *plugin; GPtrArray *checksums; - const gchar *hash = NULL; - const gchar *version = NULL; - g_autoptr(AsStore) store = NULL; + const gchar *version; + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; g_autoptr(FuDevice) device = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GString) xpath_csum = g_string_new (NULL); + g_autoptr(XbNode) csum = NULL; + g_autoptr(XbNode) release = NULL; + g_autoptr(XbSilo) silo = xb_silo_new (); g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); g_return_val_if_fail (device_id != NULL, FALSE); @@ -643,32 +707,42 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) return FALSE; /* find component in metadata */ - store = fu_engine_load_verify_store (error); - if (store == NULL) - return FALSE; - app = fu_engine_store_get_app_by_guids (store, device); - if (app == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No metadata"); - return FALSE; + version = fu_device_get_version (device); + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id); + file = g_file_new_for_path (fn); + if (g_file_query_exists (file, NULL)) { + g_autofree gchar *xpath = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + if (!xb_builder_source_load_file (source, file, + XB_BUILDER_SOURCE_FLAG_NONE, + NULL, 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; + xpath = g_strdup_printf ("component/releases/release[@version='%s']", version); + release = xb_silo_query_first (silo, xpath, NULL); } - /* find version in metadata */ - version = fu_device_get_version (device); - release = as_app_get_release (app, version); + /* try again with the system metadata */ if (release == NULL) { - /* try again with the system metadata */ - app = fu_engine_store_get_app_by_guids (self->store, device); - if (app == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No system metadata"); - return FALSE; + GPtrArray *guids = fu_device_get_guids (device); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index (guids, i); + g_autofree gchar *xpath2 = NULL; + xpath2 = g_strdup_printf ("components/component/" + "provides/firmware[@type='flashed'][text()='%s']/" + "../../releases/release[@version='%s']", + guid, version); + release = xb_silo_query_first (self->silo, xpath2, NULL); + if (release != NULL) + break; } - release = as_app_get_release (app, version); } if (release == NULL) { g_set_error (error, @@ -678,16 +752,6 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) return FALSE; } - /* find checksum */ - csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); - if (csum == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No content checksum for %s", version); - return FALSE; - } - /* get the matching checksum */ checksums = fu_device_get_checksums (device); if (checksums->len == 0) { @@ -697,29 +761,52 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) "No device checksums for %s", version); return FALSE; } + + /* do any of the checksums in the release match any in the device */ for (guint j = 0; j < checksums->len; j++) { const gchar *hash_tmp = g_ptr_array_index (checksums, j); - GChecksumType hash_kind = fwupd_checksum_guess_kind (hash_tmp); - if (as_checksum_get_kind (csum) == hash_kind) { - hash = hash_tmp; - break; + xb_string_append_union (xpath_csum, + "checksum[@target='device'][text()='%s']", + hash_tmp); + xb_string_append_union (xpath_csum, + "checksum[@target='content'][text()='%s']", + hash_tmp); + } + csum = xb_node_query_first (release, xpath_csum->str, NULL); + if (csum == NULL) { + g_autoptr(GString) checksums_device = g_string_new (NULL); + g_autoptr(GString) checksums_metadata = g_string_new (NULL); + g_autoptr(GPtrArray) csums = NULL; + g_autoptr(GString) xpath = g_string_new (NULL); + + /* get all checksums to display a useful error */ + xb_string_append_union (xpath, "checksum[@target='device']"); + xb_string_append_union (xpath, "checksum[@target='content']"); + csums = xb_node_query (release, xpath->str, 0, NULL); + if (csums == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No device or content checksum for %s", + version); + return FALSE; + } + for (guint i = 0; i < csums->len; i++) { + XbNode *csum_tmp = g_ptr_array_index (csums, i); + xb_string_append_union (checksums_metadata, + "%s", xb_node_get_text (csum_tmp)); + } + for (guint i = 0; i < checksums->len; i++) { + const gchar *hash_tmp = g_ptr_array_index (checksums, i); + xb_string_append_union (checksums_device, "%s", hash_tmp); } - } - if (hash == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No matching hash kind for %s", version); - return FALSE; - } - if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "For v%s expected %s, got %s", version, - as_checksum_get_value (csum), - hash); + checksums_metadata->str, + checksums_device->str); return FALSE; } @@ -727,46 +814,73 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) return TRUE; } -static GPtrArray * -_as_store_get_apps_by_provide (AsStore *store, AsProvideKind kind, const gchar *value) +static gboolean +fu_engine_require_vercmp (XbNode *req, const gchar *version, GError **error) { -#if AS_CHECK_VERSION(0,7,5) - return as_store_get_apps_by_provide (store, kind, value); -#else - GPtrArray *apps = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - GPtrArray *array = as_store_get_apps (store); - for (guint i = 0; i < array->len; i++) { - AsApp *app = g_ptr_array_index (array, i); - GPtrArray *provides = as_app_get_provides (app); - for (guint j = 0; j < provides->len; j++) { - AsProvide *tmp = g_ptr_array_index (provides, j); - if (kind != as_provide_get_kind (tmp)) - continue; - if (g_strcmp0 (as_provide_get_value (tmp), value) != 0) - continue; - g_ptr_array_add (apps, g_object_ref (app)); - } + 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) { + rc = fu_common_vercmp (version, version_req); + ret = rc == 0; + } else if (g_strcmp0 (tmp, "ne") == 0) { + rc = fu_common_vercmp (version, version_req); + ret = rc != 0; + } else if (g_strcmp0 (tmp, "lt") == 0) { + rc = fu_common_vercmp (version, version_req); + ret = rc < 0; + } else if (g_strcmp0 (tmp, "gt") == 0) { + rc = fu_common_vercmp (version, version_req); + ret = rc > 0; + } else if (g_strcmp0 (tmp, "le") == 0) { + rc = fu_common_vercmp (version, version_req); + ret = rc <= 0; + } else if (g_strcmp0 (tmp, "ge") == 0) { + rc = fu_common_vercmp (version, version_req); + ret = rc >= 0; + } else if (g_strcmp0 (tmp, "glob") == 0) { + ret = fnmatch (version_req, version, 0) == 0; + } else if (g_strcmp0 (tmp, "regex") == 0) { + ret = g_regex_match_simple (version_req, version, 0, 0); + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to compare [%s] and [%s]", + version_req, + version); + return FALSE; } - return apps; -#endif + + /* set error */ + if (!ret) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed predicate [%s %s %s]", + version_req, tmp, version); + } + return ret; } static gboolean -fu_engine_check_requirement_firmware (FuEngine *self, AsRequire *req, +fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req, FuDevice *device, GError **error) { g_autoptr(GError) error_local = NULL; /* old firmware version */ - if (as_require_get_value (req) == NULL) { + if (xb_node_get_text (req) == NULL) { const gchar *version = fu_device_get_version (device); - if (!as_require_version_compare (req, version, &error_local)) { - if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { + if (!fu_engine_require_vercmp (req, version, &error_local)) { + if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Not compatible with firmware version %s, requires >= %s", - version, as_require_get_version (req)); + version, xb_node_get_attr (req, "version")); } else { g_set_error (error, FWUPD_ERROR, @@ -780,15 +894,15 @@ fu_engine_check_requirement_firmware (FuEngine *self, AsRequire *req, } /* bootloader version */ - if (g_strcmp0 (as_require_get_value (req), "bootloader") == 0) { + if (g_strcmp0 (xb_node_get_text (req), "bootloader") == 0) { const gchar *version = fu_device_get_version_bootloader (device); - if (!as_require_version_compare (req, version, &error_local)) { - if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { + if (!fu_engine_require_vercmp (req, version, &error_local)) { + if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Not compatible with bootloader version %s, requires >= %s", - version, as_require_get_version (req)); + version, xb_node_get_attr (req, "version")); } else { g_set_error (error, FWUPD_ERROR, @@ -802,15 +916,15 @@ fu_engine_check_requirement_firmware (FuEngine *self, AsRequire *req, } /* vendor ID */ - if (g_strcmp0 (as_require_get_value (req), "vendor-id") == 0) { + if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0) { const gchar *version = fu_device_get_vendor_id (device); - if (!as_require_version_compare (req, version, &error_local)) { - if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { + if (!fu_engine_require_vercmp (req, version, &error_local)) { + if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Not compatible with vendor %s, requires >= %s", - version, as_require_get_version (req)); + version, xb_node_get_attr (req, "version")); } else { g_set_error (error, FWUPD_ERROR, @@ -824,8 +938,8 @@ fu_engine_check_requirement_firmware (FuEngine *self, AsRequire *req, } /* another device */ - if (as_utils_guid_is_valid (as_require_get_value (req))) { - const gchar *guid = as_require_get_value (req); + if (fu_common_guid_is_valid (xb_node_get_text (req))) { + const gchar *guid = xb_node_get_text (req); const gchar *version; g_autoptr(FuDevice) device2 = NULL; @@ -836,15 +950,15 @@ fu_engine_check_requirement_firmware (FuEngine *self, AsRequire *req, /* get the version of the other device */ version = fu_device_get_version (device2); - if (!as_require_version_compare (req, version, &error_local)) { - if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { + if (!fu_engine_require_vercmp (req, version, &error_local)) { + if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Not compatible with %s version %s, requires >= %s", fu_device_get_name (device2), version, - as_require_get_version (req)); + xb_node_get_attr (req, "version")); } else { g_set_error (error, FWUPD_ERROR, @@ -863,57 +977,57 @@ fu_engine_check_requirement_firmware (FuEngine *self, AsRequire *req, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "cannot handle firmware requirement %s", - as_require_get_value (req)); + "cannot handle firmware requirement '%s'", + xb_node_get_text (req)); return FALSE; } static gboolean -fu_engine_check_requirement_id (FuEngine *self, AsRequire *req, GError **error) +fu_engine_check_requirement_id (FuEngine *self, XbNode *req, GError **error) { g_autoptr(GError) error_local = NULL; const gchar *version = g_hash_table_lookup (self->runtime_versions, - as_require_get_value (req)); + xb_node_get_text (req)); if (version == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no version available for %s", - as_require_get_value (req)); + xb_node_get_text (req)); return FALSE; } - if (!as_require_version_compare (req, version, &error_local)) { - if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { + if (!fu_engine_require_vercmp (req, version, &error_local)) { + if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Not compatible with %s version %s, requires >= %s", - as_require_get_value (req), version, - as_require_get_version (req)); + 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", - as_require_get_value (req), error_local->message); + xb_node_get_text (req), error_local->message); } return FALSE; } g_debug ("requirement %s %s %s on %s passed", - as_require_get_version (req), - as_require_compare_to_string (as_require_get_compare (req)), - version, as_require_get_value (req)); + xb_node_get_attr (req, "version"), + xb_node_get_attr (req, "compare"), + version, xb_node_get_text (req)); return TRUE; } static gboolean -fu_engine_check_requirement_hardware (FuEngine *self, AsRequire *req, GError **error) +fu_engine_check_requirement_hardware (FuEngine *self, XbNode *req, GError **error) { g_auto(GStrv) hwid_split = NULL; /* split and treat as OR */ - hwid_split = g_strsplit (as_require_get_value (req), "|", -1); + hwid_split = g_strsplit (xb_node_get_text (req), "|", -1); for (guint i = 0; hwid_split[i] != NULL; i++) { if (fu_hwids_has_guid (self->hwids, hwid_split[i])) { g_debug ("HWID provided %s", hwid_split[i]); @@ -926,26 +1040,26 @@ fu_engine_check_requirement_hardware (FuEngine *self, AsRequire *req, GError **e FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "no HWIDs matched %s", - as_require_get_value (req)); + xb_node_get_text (req)); return FALSE; } static gboolean -fu_engine_check_requirement (FuEngine *self, AsRequire *req, FuDevice *device, GError **error) +fu_engine_check_requirement (FuEngine *self, XbNode *req, FuDevice *device, GError **error) { /* ensure component requirement */ - if (as_require_get_kind (req) == AS_REQUIRE_KIND_ID) + if (g_strcmp0 (xb_node_get_element (req), "id") == 0) return fu_engine_check_requirement_id (self, req, error); /* ensure firmware requirement */ - if (as_require_get_kind (req) == AS_REQUIRE_KIND_FIRMWARE) { + if (g_strcmp0 (xb_node_get_element (req), "firmware") == 0) { if (device == NULL) return TRUE; return fu_engine_check_requirement_firmware (self, req, device, error); } /* ensure hardware requirement */ - if (as_require_get_kind (req) == AS_REQUIRE_KIND_HARDWARE) + if (g_strcmp0 (xb_node_get_element (req), "hardware") == 0) return fu_engine_check_requirement_hardware (self, req, error); /* not supported */ @@ -953,7 +1067,7 @@ fu_engine_check_requirement (FuEngine *self, AsRequire *req, FuDevice *device, G FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "cannot handle requirement type %s", - as_require_kind_to_string (as_require_get_kind (req))); + xb_node_get_element (req)); return FALSE; } @@ -961,8 +1075,9 @@ gboolean fu_engine_check_requirements (FuEngine *self, FuInstallTask *task, FwupdInstallFlags flags, GError **error) { - GPtrArray *reqs = as_app_get_requires (fu_install_task_get_app (task)); FuDevice *device = fu_install_task_get_device (task); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) reqs = NULL; /* all install task checks require a device */ if (device != NULL) { @@ -971,121 +1086,28 @@ fu_engine_check_requirements (FuEngine *self, FuInstallTask *task, } /* do engine checks */ + reqs = xb_node_query (fu_install_task_get_component (task), + "requires/*", 0, &error_local); + if (reqs == NULL) { + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + return TRUE; + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) + return TRUE; + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } for (guint i = 0; i < reqs->len; i++) { - AsRequire *req = g_ptr_array_index (reqs, i); + XbNode *req = g_ptr_array_index (reqs, i); if (!fu_engine_check_requirement (self, req, device, error)) return FALSE; } return TRUE; } -static void -fu_engine_vendor_fixup_provide_value (AsApp *app) +void +fu_engine_idle_reset (FuEngine *self) { - GPtrArray *provides; - - /* no quirk required */ - if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE) - return; - - /* fix each provide to be a GUID */ - provides = as_app_get_provides (app); - for (guint i = 0; i < provides->len; i++) { - AsProvide *prov = g_ptr_array_index (provides, i); - const gchar *value = as_provide_get_value (prov); - g_autofree gchar *guid = NULL; - if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; - if (as_utils_guid_is_valid (value)) - continue; - guid = as_utils_guid_from_string (value); - as_provide_set_value (prov, guid); - } -} - -static void -fu_engine_vendor_quirk_release_version (FuEngine *self, AsApp *app) -{ - AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET; - GPtrArray *releases; - const gchar *quirk; - const gchar *version_format; - - /* no quirk required */ - if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE) - return; - - /* fall back to the quirk database until all files have metadata */ - quirk = fu_quirks_lookup_by_id (self->quirks, - "DaemonVersionFormat=quad", - FU_QUIRKS_DAEMON_VERSION_FORMAT); - if (quirk != NULL) { - g_auto(GStrv) globs = g_strsplit (quirk, ",", -1); - for (guint i = 0; globs[i] != NULL; i++) { - if (fnmatch (globs[i], as_app_get_id (app), 0) == 0) { - flags = AS_VERSION_PARSE_FLAG_NONE; - break; - } - } - } - - /* specified in metadata */ - version_format = as_app_get_metadata_item (app, "LVFS::VersionFormat"); - if (g_strcmp0 (version_format, "quad") == 0) - flags = AS_VERSION_PARSE_FLAG_NONE; - - /* fix each release */ - releases = as_app_get_releases (app); - for (guint i = 0; i < releases->len; i++) { - AsRelease *rel; - const gchar *version; - guint64 ver_uint32; - g_autofree gchar *version_new = NULL; - - rel = g_ptr_array_index (releases, i); - version = as_release_get_version (rel); - if (version == NULL) - continue; - if (g_strstr_len (version, -1, ".") != NULL) - continue; - - /* metainfo files use hex and the LVFS uses decimal */ - ver_uint32 = fu_common_strtoull (version); - if (ver_uint32 == 0) - continue; - - /* convert to dotted decimal */ - version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags); - as_release_set_version (rel, version_new); - } -} - -static gchar * -fu_engine_get_guids_from_store (AsStore *store) -{ - AsProvide *prov; - GPtrArray *provides; - GPtrArray *apps; - GString *str = g_string_new (""); - - /* return a string with all the firmware apps in the store */ - apps = as_store_get_apps (store); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = AS_APP (g_ptr_array_index (apps, i)); - provides = as_app_get_provides (app); - for (guint j = 0; j < provides->len; j++) { - prov = AS_PROVIDE (g_ptr_array_index (provides, j)); - if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; - g_string_append_printf (str, "%s,", as_provide_get_value (prov)); - } - } - if (str->len == 0) { - g_string_free (str, TRUE); - return NULL; - } - g_string_truncate (str, str->len - 1); - return g_string_free (str, FALSE); + fu_idle_reset (self->idle); } static gchar * @@ -1217,9 +1239,14 @@ fu_engine_install_tasks (FuEngine *self, FwupdInstallFlags flags, GError **error) { + g_autoptr(FuIdleLocker) locker = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) devices_new = NULL; + /* do not allow auto-shutdown during this time */ + locker = fu_idle_locker_new (self->idle, "performing update"); + g_assert (locker != NULL); + /* notify the plugins about the composite action */ devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < install_tasks->len; i++) { @@ -1244,6 +1271,13 @@ fu_engine_install_tasks (FuEngine *self, } } + /* set all the device statuses back to unknown */ + for (guint i = 0; i < install_tasks->len; i++) { + FuInstallTask *task = g_ptr_array_index (install_tasks, i); + FuDevice *device = fu_install_task_get_device (task); + fu_device_set_status (device, FWUPD_STATUS_UNKNOWN); + } + /* get a new list of devices in case they replugged */ devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < devices->len; i++) { @@ -1295,27 +1329,28 @@ fu_engine_install (FuEngine *self, FwupdInstallFlags flags, GError **error) { - AsApp *app = fu_install_task_get_app (task); - AsChecksum *csum_tmp; - AsRelease *rel; + XbNode *component = fu_install_task_get_component (task); FuDevice *device = fu_install_task_get_device (task); GBytes *blob_fw; - const gchar *tmp; - const gchar *version; + const gchar *tmp = NULL; + g_autofree gchar *release_key = NULL; + g_autofree gchar *version_rel = NULL; g_autoptr(GBytes) blob_fw2 = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(XbNode) rel = NULL; g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); - g_return_val_if_fail (AS_IS_APP (app), FALSE); + g_return_val_if_fail (XB_IS_NODE (component), FALSE); g_return_val_if_fail (blob_cab != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* not in bootloader mode */ if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { const gchar *caption = NULL; - AsScreenshot *ss = as_app_get_screenshot_default (app); - if (ss != NULL) - caption = as_screenshot_get_caption (ss, NULL); + caption = xb_node_query_text (component, + "screenshots/screenshot/caption", + NULL); if (caption != NULL) { g_set_error (error, FWUPD_ERROR, @@ -1333,40 +1368,36 @@ fu_engine_install (FuEngine *self, } /* parse the DriverVer */ - rel = as_app_get_release_default (app); + rel = xb_node_query_first (component, "releases/release", &error_local); if (rel == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "No releases in the firmware component"); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "No releases in the firmware component: %s", + error_local->message); return FALSE; } /* get the blob */ - csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); - tmp = as_checksum_get_filename (csum_tmp); - if (tmp == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "No checksum filename"); - return FALSE; - } + tmp = xb_node_query_attr (rel, "checksum[@target='content']", "filename", NULL); + if (tmp == NULL) + tmp = "firmware.bin"; /* not all devices have to use the same blob */ - blob_fw = as_release_get_blob (rel, tmp); + release_key = g_strdup_printf ("fwupd::ReleaseBlob(%s)", tmp); + blob_fw = xb_node_get_data (rel, release_key); if (blob_fw == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "Failed to get firmware blob"); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "Failed to get firmware blob using %s", tmp); return FALSE; } /* use a bubblewrap helper script to build the firmware */ - tmp = as_app_get_metadata_item (app, "fwupd::BuilderScript"); + tmp = g_object_get_data (G_OBJECT (component), "fwupd::BuilderScript"); if (tmp != NULL) { - const gchar *tmp2 = as_app_get_metadata_item (app, "fwupd::BuilderOutput"); + const gchar *tmp2 = g_object_get_data (G_OBJECT (component), "fwupd::BuilderOutput"); if (tmp2 == NULL) tmp2 = "firmware.bin"; blob_fw2 = fu_common_firmware_builder (blob_fw, tmp, tmp2, error); @@ -1377,9 +1408,9 @@ fu_engine_install (FuEngine *self, } /* install firmware blob */ - version = as_release_get_version (rel); + version_rel = fu_engine_get_release_version (self, component, rel); return fu_engine_install_blob (self, device, blob_cab, blob_fw2, - version, flags, error); + version_rel, flags, error); } /** @@ -1399,6 +1430,38 @@ fu_engine_get_plugins (FuEngine *self) return fu_plugin_list_get_all (self->plugin_list); } +static FuDevice * +fu_engine_get_device_by_id (FuEngine *self, const gchar *device_id, GError **error) +{ + g_autoptr(FuDevice) device1 = NULL; + g_autoptr(FuDevice) device2 = NULL; + + /* find device */ + device1 = fu_device_list_get_by_id (self->device_list, device_id, error); + if (device1 == NULL) + return NULL; + + /* no replug required */ + if (!fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) + return g_steal_pointer (&device1); + + /* wait for device to disconnect and reconnect */ + if (!fu_device_list_wait_for_replug (self->device_list, device1, error)) { + g_prefix_error (error, "failed to wait for detach replug: "); + return NULL; + } + + /* get the new device */ + device2 = fu_device_list_get_by_id (self->device_list, device_id, error); + if (device2 == NULL) { + g_prefix_error (error, "failed to get device after replug: "); + return NULL; + } + + /* success */ + return g_steal_pointer (&device2); +} + gboolean fu_engine_install_blob (FuEngine *self, FuDevice *device_orig, @@ -1416,8 +1479,14 @@ fu_engine_install_blob (FuEngine *self, g_autoptr(FwupdRelease) release_history = fwupd_release_new (); g_autoptr(GError) error_local = NULL; g_autoptr(GHashTable) metadata_hash = NULL; + g_autoptr(GHashTable) os_release = NULL; g_autoptr(GTimer) timer = g_timer_new (); + /* add release data from os-release */ + os_release = fwupd_get_os_release (error); + if (os_release == NULL) + return FALSE; + /* test the firmware is not an empty blob */ if (g_bytes_get_size (blob_fw2) == 0) { g_set_error (error, @@ -1459,21 +1528,9 @@ fu_engine_install_blob (FuEngine *self, /* in case another device caused us to go into replug before starting */ g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) { - g_prefix_error (error, "failed to get device ID after detach: "); + device = fu_engine_get_device_by_id (self, device_id_orig, error); + if (device == NULL) return FALSE; - } - if (!fu_device_list_wait_for_replug (self->device_list, device, error)) { - g_prefix_error (error, "failed to wait for detach replug: "); - return FALSE; - } - g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) { - g_prefix_error (error, "failed to get device ID after detach replug: "); - return FALSE; - } /* mark this as modified even if we actually fail to do the update */ fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC); @@ -1486,17 +1543,36 @@ fu_engine_install_blob (FuEngine *self, /* add device to database */ if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { + const gchar *tmp; g_autofree gchar *checksum = NULL; checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob_cab); fwupd_release_set_version (release_history, version); fwupd_release_add_checksum (release_history, checksum); fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); + + /* add details from os-release as metadata */ + tmp = g_hash_table_lookup (os_release, "ID"); + if (tmp != NULL) { + fwupd_release_add_metadata_item (release_history, + "DistroId", tmp); + } + tmp = g_hash_table_lookup (os_release, "VERSION_ID"); + if (tmp != NULL) { + fwupd_release_add_metadata_item (release_history, + "DistroVersion", tmp); + } + tmp = g_hash_table_lookup (os_release, "VARIANT_ID"); + if (tmp != NULL) { + fwupd_release_add_metadata_item (release_history, + "DistroVariant", tmp); + } if (!fu_history_add_device (self->history, device, release_history, error)) return FALSE; } /* do the update */ if (!fu_plugin_runner_update_detach (plugin, device, &error_local)) { + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); fu_device_set_update_error (device, error_local->message); if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && !fu_history_modify_device (self->history, device, @@ -1508,21 +1584,18 @@ fu_engine_install_blob (FuEngine *self, return FALSE; } g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); + device = fu_engine_get_device_by_id (self, device_id_orig, error); if (device == NULL) { - g_prefix_error (error, "failed to get device ID after detach: "); + g_prefix_error (error, "failed to get device after detach: "); return FALSE; } - if (!fu_device_list_wait_for_replug (self->device_list, device, error)) { - g_prefix_error (error, "failed to wait for detach replug: "); + + /* get new plugin after detach */ + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (device), + error); + if (plugin == NULL) return FALSE; - } - g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) { - g_prefix_error (error, "failed to get device ID after detach replug: "); - return FALSE; - } if (!fu_plugin_runner_update (plugin, device, blob_cab, @@ -1532,6 +1605,7 @@ fu_engine_install_blob (FuEngine *self, g_autoptr(GError) error_attach = NULL; /* save to database */ + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); fu_device_set_update_error (device, error_local->message); if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && !fu_history_modify_device (self->history, device, @@ -1564,22 +1638,13 @@ fu_engine_install_blob (FuEngine *self, return FALSE; } g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); + device = fu_engine_get_device_by_id (self, device_id_orig, error); if (device == NULL) { - g_prefix_error (error, "failed to get device ID after update: "); - return FALSE; - } - if (!fu_device_list_wait_for_replug (self->device_list, device, error)) { - g_prefix_error (error, "failed to wait for replug after update: "); - return FALSE; - } - g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) { - g_prefix_error (error, "failed to get device ID after post-update restart: "); + g_prefix_error (error, "failed to get device after update: "); return FALSE; } if (!fu_plugin_runner_update_attach (plugin, device, &error_local)) { + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); fu_device_set_update_error (device, error_local->message); if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && !fu_history_modify_device (self->history, device, @@ -1592,23 +1657,20 @@ fu_engine_install_blob (FuEngine *self, return FALSE; } g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); + device = fu_engine_get_device_by_id (self, device_id_orig, error); if (device == NULL) { - g_prefix_error (error, "failed to get device ID after attach: "); - return FALSE; - } - if (!fu_device_list_wait_for_replug (self->device_list, device, error)) { - g_prefix_error (error, "failed to wait for replug after attach: "); + g_prefix_error (error, "failed to get device after attach: "); return FALSE; } - /* get the new version number */ - g_clear_object (&device); - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) { - g_prefix_error (error, "failed to get device ID after attach replug: "); + /* get new plugin after attach */ + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (device), + error); + if (plugin == NULL) return FALSE; - } + + /* get the new version number */ if (!fu_plugin_runner_update_reload (plugin, device, error)) { g_prefix_error (error, "failed to reload device: "); return FALSE; @@ -1631,7 +1693,8 @@ fu_engine_install_blob (FuEngine *self, g_timer_elapsed (timer, NULL)); /* update database */ - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) { + 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, FWUPD_UPDATE_STATE_NEEDS_REBOOT); if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && !fu_history_modify_device (self->history, device, @@ -1646,6 +1709,7 @@ fu_engine_install_blob (FuEngine *self, if (version != NULL && g_strcmp0 (version_orig, version) != 0 && g_strcmp0 (version_orig, fu_device_get_version (device)) == 0) { + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); fu_device_set_update_error (device, "device version not updated on success"); if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && !fu_history_modify_device (self->history, device, @@ -1665,12 +1729,12 @@ fu_engine_install_blob (FuEngine *self, } /* success */ + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS); if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && !fu_history_modify_device (self->history, device, FU_HISTORY_FLAGS_MATCH_NEW_VERSION, error)) return FALSE; - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS); return TRUE; } @@ -1727,116 +1791,159 @@ fu_engine_get_item_by_id_fallback_history (FuEngine *self, const gchar *id, GErr return NULL; } -/* add releases that do not exist from a higher priority remote */ -static void -fu_engine_merge_component_releases (AsApp *app_old, AsApp *app) +/* for the self tests */ +void +fu_engine_set_silo (FuEngine *self, XbSilo *silo) { - GPtrArray *releases = as_app_get_releases (app); - for (guint j = 0; j < releases->len; j++) { - AsRelease *release = g_ptr_array_index (releases, j); - AsRelease *release_old; - const gchar *version = as_release_get_version (release); - release_old = as_app_get_release_by_version (app_old, version); - if (release_old != NULL) - continue; - g_debug ("adding release %s to existing %s", - version, as_app_get_id (app_old)); - as_app_add_release (app_old, release); - } -} - -gboolean -fu_engine_load_metadata_from_file (FuEngine *self, - const gchar *path, - const gchar *remote_id, - GError **error) -{ - GPtrArray *apps; - g_autoptr(AsStore) store = NULL; - g_autoptr(GFile) file = NULL; - g_autoptr(GBytes) remote_blob = NULL; - g_autoptr(GPtrArray) apps_new = NULL; - - /* load the store locally until we know it is valid */ - store = as_store_new (); - file = g_file_new_for_path (path); - if (!as_store_from_file (store, file, NULL, NULL, error)) - return FALSE; - - /* save the remote to the release */ - if (remote_id != NULL && remote_id[0] != '\0') - remote_blob = g_bytes_new (remote_id, strlen (remote_id) + 1); - - /* add the new application from the store */ - apps = as_store_get_apps (store); - apps_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); - AsApp *app_old = as_store_get_app_by_id (self->store, as_app_get_id (app)); - - /* save the remote-id to all the releases for this component */ - if (remote_blob != NULL) { - GPtrArray *releases = as_app_get_releases (app); - for (guint j = 0; j < releases->len; j++) { - AsRelease *release = g_ptr_array_index (releases, j); - as_release_set_blob (release, - "fwupd::RemoteId", - remote_blob); - } - } - - /* possibly convert the version from 0x to dotted */ - fu_engine_vendor_quirk_release_version (self, app); - - /* possibly convert the flashed provide to a GUID */ - fu_engine_vendor_fixup_provide_value (app); - - /* merge in new releases if already known */ - if (app_old != NULL) { - fu_engine_merge_component_releases (app_old, app); - continue; - } - g_ptr_array_add (apps_new, g_object_ref (app)); - } - - /* add in one operation to avoid 'n' "Emitting ::changed()" events */ - as_store_add_apps (self->store, apps_new); - return TRUE; + g_return_if_fail (FU_IS_ENGINE (self)); + g_return_if_fail (XB_IS_SILO (silo)); + g_set_object (&self->silo, silo); } static gboolean fu_engine_is_device_supported (FuEngine *self, FuDevice *device) { - AsApp *app; + g_autoptr(XbNode) component = NULL; + + /* sanity check */ + if (self->silo == NULL) { + g_critical ("FuEngine silo not set up"); + return FALSE; + } /* no device version */ if (fu_device_get_version (device) == NULL) return FALSE; /* match the GUIDs in the XML */ - app = fu_engine_store_get_app_by_guids (self->store, device); - if (app == NULL) + component = fu_engine_get_component_by_guids (self, device); + if (component == NULL) return FALSE; /* success */ return TRUE; } +static gboolean +fu_engine_appstream_upgrade_cb (XbBuilderFixup *self, + XbBuilderNode *bn, + gpointer user_data, + GError **error) +{ + if (g_strcmp0 (xb_builder_node_get_element (bn), "metadata") == 0) + xb_builder_node_set_element (bn, "custom"); + return TRUE; +} + +static XbBuilderSource * +fu_engine_create_metadata_builder_source (FuEngine *self, + const gchar *fn, + GError **error) +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autofree gchar *xml = NULL; + + g_debug ("building metadata for %s", fn); + blob = fu_common_get_contents_bytes (fn, error); + if (blob == NULL) + return NULL; + + /* convert the silo for the CAB into a XbBuilderSource */ + silo = fu_engine_get_silo_from_blob (self, blob, error); + if (silo == NULL) + return NULL; + xml = xb_silo_export (silo, XB_NODE_EXPORT_FLAG_NONE, error); + if (xml == NULL) + return NULL; + if (!xb_builder_source_load_xml (source, xml, + XB_BUILDER_SOURCE_FLAG_NONE, + error)) + return NULL; + return g_steal_pointer (&source); +} + +static gboolean +fu_engine_create_metadata (FuEngine *self, XbBuilder *builder, + FwupdRemote *remote, GError **error) +{ + g_autoptr(GPtrArray) files = NULL; + const gchar *path; + + /* find all files in directory */ + path = fwupd_remote_get_filename_cache (remote); + files = fu_common_get_files_recursive (path, error); + if (files == NULL) + return FALSE; + + /* add each source */ + for (guint i = 0; i < files->len; i++) { + g_autoptr(XbBuilderNode) custom = NULL; + g_autoptr(XbBuilderSource) source = NULL; + g_autoptr(GError) error_local = NULL; + const gchar *fn = g_ptr_array_index (files, i); + + /* check is cab file */ + if (!g_str_has_suffix (fn, ".cab")) { + g_debug ("ignoring: %s", fn); + continue; + } + + /* build source for file */ + source = fu_engine_create_metadata_builder_source (self, fn, &error_local); + if (source == NULL) { + g_warning ("%s", error_local->message); + continue; + } + + /* add metadata */ + custom = xb_builder_node_new ("custom"); + xb_builder_node_insert_text (custom, + "value", fn, + "key", "fwupd::FilenameCache", + NULL); + xb_builder_node_insert_text (custom, + "value", fwupd_remote_get_id (remote), + "key", "fwupd::RemoteId", + NULL); + xb_builder_source_set_info (source, custom); + xb_builder_import_source (builder, source); + } + return TRUE; +} + static gboolean fu_engine_load_metadata_store (FuEngine *self, GError **error) { GPtrArray *remotes; - g_autofree gchar *guids_str = NULL; + g_autofree gchar *cachedirpkg = NULL; + g_autofree gchar *xmlbfn = NULL; + g_autoptr(GFile) xmlb = NULL; g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); - /* clear existing store */ - as_store_remove_all (self->store); + /* clear existing silo */ + g_clear_object (&self->silo); + + /* verbose profiling */ + if (g_getenv ("FWUPD_VERBOSE") != NULL) { + xb_builder_set_profile_flags (builder, + XB_SILO_PROFILE_FLAG_XPATH | + XB_SILO_PROFILE_FLAG_DEBUG); + } /* load each enabled metadata file */ remotes = fu_config_get_remotes (self->config); for (guint i = 0; i < remotes->len; i++) { const gchar *path = NULL; g_autoptr(GError) error_local = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(XbBuilderFixup) fixup = NULL; + g_autoptr(XbBuilderNode) custom = NULL; + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + FwupdRemote *remote = g_ptr_array_index (remotes, i); if (!fwupd_remote_get_enabled (remote)) { g_debug ("remote %s not enabled, so skipping", @@ -1848,29 +1955,77 @@ fu_engine_load_metadata_store (FuEngine *self, GError **error) g_debug ("no %s, so skipping", path); continue; } - if (!fu_engine_load_metadata_from_file (self, path, - fwupd_remote_get_id (remote), - &error_local)) { + + /* generate all metadata on demand */ + if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + g_debug ("building metadata for remote '%s'", + fwupd_remote_get_id (remote)); + if (!fu_engine_create_metadata (self, builder, remote, &error_local)) { + g_warning ("failed to generate remote %s: %s", + fwupd_remote_get_id (remote), + error_local->message); + } + continue; + } + + /* save the remote-id in the custom metadata space */ + file = g_file_new_for_path (path); + if (!xb_builder_source_load_file (source, file, + XB_BUILDER_SOURCE_FLAG_NONE, + NULL, &error_local)) { g_warning ("failed to load remote %s: %s", fwupd_remote_get_id (remote), error_local->message); continue; } + + /* fix up any legacy installed files */ + fixup = xb_builder_fixup_new ("AppStreamUpgrade", + fu_engine_appstream_upgrade_cb, + self, NULL); + xb_builder_fixup_set_max_depth (fixup, 3); + xb_builder_source_add_fixup (source, fixup); + + /* add metadata */ + custom = xb_builder_node_new ("custom"); + xb_builder_node_insert_text (custom, + "value", path, + "key", "fwupd::FilenameCache", + NULL); + xb_builder_node_insert_text (custom, + "value", fwupd_remote_get_id (remote), + "key", "fwupd::RemoteId", + NULL); + xb_builder_source_set_info (source, custom); + + /* we need to watch for changes? */ + xb_builder_import_source (builder, source); } + /* ensure silo is up to date */ + cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG); + xmlbfn = g_build_filename (cachedirpkg, "metadata.xmlb", NULL); + xmlb = g_file_new_for_path (xmlbfn); + self->silo = xb_builder_ensure (builder, xmlb, + XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID, + NULL, error); + if (self->silo == NULL) + return FALSE; + /* print what we've got */ - g_debug ("%u components now in store", as_store_get_size (self->store)); + components = xb_silo_query (self->silo, "components/component", 0, NULL); + if (components != NULL) + g_debug ("%u components now in silo", components->len); - /* update the list of supported GUIDs */ - g_ptr_array_set_size (self->supported_guids, 0); - guids_str = fu_engine_get_guids_from_store (self->store); - if (guids_str != NULL) { - g_auto(GStrv) guids = g_strsplit (guids_str, ",", -1); - for (guint i = 0; guids[i] != NULL; i++) { - g_ptr_array_add (self->supported_guids, - g_steal_pointer (&guids[i])); - } - } + /* build the index */ + if (!xb_silo_query_build_index (self->silo, + "components/component/provides/firmware", + "type", error)) + return FALSE; + if (!xb_silo_query_build_index (self->silo, + "components/component/provides/firmware", + NULL, error)) + return FALSE; /* did any devices SUPPORTED state change? */ devices = fu_device_list_get_all (self->device_list); @@ -2044,21 +2199,19 @@ fu_engine_update_metadata (FuEngine *self, const gchar *remote_id, } /** - * fu_engine_get_store_from_blob: + * fu_engine_get_silo_from_blob: * @self: A #FuEngine * @blob_cab: A #GBytes * @error: A #GError, or %NULL * - * Creates an AppStream store from a .cab file blob. + * Creates a silo from a .cab file blob. * - * Returns: (transfer container): a #AsStore, or %NULL + * Returns: (transfer container): a #XbSilo, or %NULL **/ -AsStore * -fu_engine_get_store_from_blob (FuEngine *self, GBytes *blob_cab, GError **error) +XbSilo * +fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, GError **error) { - GPtrArray *apps; - g_autofree gchar *checksum = NULL; - g_autoptr(AsStore) store = NULL; + g_autoptr(XbSilo) silo = NULL; g_return_val_if_fail (FU_IS_ENGINE (self), NULL); g_return_val_if_fail (blob_cab != NULL, NULL); @@ -2066,56 +2219,47 @@ fu_engine_get_store_from_blob (FuEngine *self, GBytes *blob_cab, GError **error) /* load file */ fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING); - store = fu_common_store_from_cab_bytes (blob_cab, - fu_engine_get_archive_size_max (self), - error); - if (store == NULL) + silo = fu_common_cab_build_silo (blob_cab, + fu_engine_get_archive_size_max (self), + error); + if (silo == NULL) return NULL; - /* fix all the apps */ - apps = as_store_get_apps (store); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); - - /* possibly convert the version from 0x to dotted */ - fu_engine_vendor_quirk_release_version (self, app); - - /* possibly convert the flashed provide to a GUID */ - fu_engine_vendor_fixup_provide_value (app); - } - - /* get a checksum of the file and use it as the origin */ - checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA256, blob_cab); - as_store_set_origin (store, checksum); - fu_engine_set_status (self, FWUPD_STATUS_IDLE); - return g_steal_pointer (&store); + return g_steal_pointer (&silo); } static FwupdDevice * -fu_engine_get_result_from_app (FuEngine *self, AsApp *app, GError **error) +fu_engine_get_result_from_component (FuEngine *self, XbNode *component, GError **error) { FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE; - AsRelease *release; - GPtrArray *provides; g_autoptr(FuInstallTask) task = NULL; g_autoptr(FwupdDevice) dev = NULL; g_autoptr(FwupdRelease) rel = NULL; g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) provides = NULL; + g_autoptr(XbNode) description = NULL; + g_autoptr(XbNode) release = NULL; dev = fwupd_device_new (); - provides = as_app_get_provides (app); + provides = xb_node_query (component, + "provides/firmware[@type=$'flashed']", + 0, &error_local); + if (provides == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get release: %s", + error_local->message); + return NULL; + } for (guint i = 0; i < provides->len; i++) { - AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i)); + XbNode *prov = XB_NODE (g_ptr_array_index (provides, i)); const gchar *guid; g_autoptr(FuDevice) device = NULL; - /* not firmware */ - if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; - /* is a online or offline update appropriate */ - guid = as_provide_get_value (prov); + guid = xb_node_get_text (prov); if (guid == NULL) continue; device = fu_device_list_get_by_guid (self->device_list, guid, NULL); @@ -2137,14 +2281,24 @@ fu_engine_get_result_from_app (FuEngine *self, AsApp *app, GError **error) } /* check we can install it */ - task = fu_install_task_new (NULL, app); + task = fu_install_task_new (NULL, component); if (!fu_engine_check_requirements (self, task, FWUPD_INSTALL_FLAG_NONE, error)) return NULL; /* verify trust */ - release = as_app_get_release_default (app); + release = xb_node_query_first (component, + "releases/release", + &error_local); + if (release == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get release: %s", + error_local->message); + return NULL; + } if (!fu_keyring_get_release_trust_flags (release, &trust_flags, &error_local)) { @@ -2160,10 +2314,18 @@ fu_engine_get_result_from_app (FuEngine *self, AsApp *app, GError **error) } /* create a result with all the metadata in */ - fwupd_device_set_description (dev, as_app_get_description (app, NULL)); + description = xb_node_query_first (component, "description", NULL); + if (description != NULL) { + g_autofree gchar *xml = NULL; + xml = xb_node_export (description, + XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, + NULL); + if (xml != NULL) + fwupd_device_set_description (dev, xml); + } rel = fwupd_release_new (); fwupd_release_set_trust_flags (rel, trust_flags); - fu_engine_set_release_from_appstream (self, rel, app, release); + fu_engine_set_release_from_appstream (self, rel, component, release); fwupd_device_add_release (dev, rel); return g_steal_pointer (&dev); } @@ -2183,53 +2345,61 @@ fu_engine_get_result_from_app (FuEngine *self, AsApp *app, GError **error) GPtrArray * fu_engine_get_details (FuEngine *self, gint fd, GError **error) { - GPtrArray *apps; const gchar *remote_id; g_autofree gchar *csum = NULL; - g_autoptr(AsStore) store = NULL; g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) components = NULL; g_autoptr(GPtrArray) details = NULL; + g_autoptr(XbSilo) silo = NULL; g_return_val_if_fail (FU_IS_ENGINE (self), NULL); g_return_val_if_fail (fd > 0, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - /* get all apps */ + /* get all components */ blob = fu_common_get_contents_fd (fd, fu_engine_get_archive_size_max (self), error); if (blob == NULL) return NULL; - store = fu_engine_get_store_from_blob (self, blob, error); - if (store == NULL) + silo = fu_engine_get_silo_from_blob (self, blob, error); + if (silo == NULL) return NULL; - apps = as_store_get_apps (store); - if (apps->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no components"); + components = xb_silo_query (silo, "components/component", 0, &error_local); + if (components == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no components: %s", + error_local->message); return NULL; } + /* build the index */ + if (!xb_silo_query_build_index (silo, "components/component/provides/firmware", + "type", error)) + return FALSE; + if (!xb_silo_query_build_index (silo, "components/component/provides/firmware", + NULL, error)) + return FALSE; + /* does this exist in any enabled remote */ csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob); remote_id = fu_engine_get_remote_id_for_checksum (self, csum); /* create results with all the metadata in */ details = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); FwupdDevice *dev; - - as_app_set_origin (app, as_store_get_origin (store)); - dev = fu_engine_get_result_from_app (self, app, error); + dev = fu_engine_get_result_from_component (self, component, error); if (dev == NULL) return NULL; if (remote_id != NULL) { FwupdRelease *rel = fwupd_device_get_release_default (dev); fwupd_release_set_remote_id (rel, remote_id); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED); + fwupd_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED); } g_ptr_array_add (details, dev); } @@ -2384,44 +2554,106 @@ fu_engine_get_remotes (FuEngine *self, GError **error) return g_ptr_array_ref (remotes); } +/** + * fu_engine_get_remote_by_id: + * @self: A #FuEngine + * @remote_id: A string representation of a remote + * @error: A #GError, or %NULL + * + * Gets the FwupdRemote object. + * + * Returns: FwupdRemote + **/ +FwupdRemote * +fu_engine_get_remote_by_id (FuEngine *self, const gchar *remote_id, GError **error) +{ + g_autoptr(GPtrArray) remotes = NULL; + + remotes = fu_engine_get_remotes (self, error); + if (remotes == NULL) + return NULL; + + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index (remotes, i); + if (g_strcmp0 (remote_id, fwupd_remote_get_id (remote)) == 0) + return remote; + } + + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Couldn't find remote %s", remote_id); + + return NULL; +} + + static gint fu_engine_sort_releases_cb (gconstpointer a, gconstpointer b) { FwupdRelease *rel_a = FWUPD_RELEASE (*((FwupdRelease **) a)); FwupdRelease *rel_b = FWUPD_RELEASE (*((FwupdRelease **) b)); - return as_utils_vercmp (fwupd_release_get_version (rel_b), + return fu_common_vercmp (fwupd_release_get_version (rel_b), fwupd_release_get_version (rel_a)); } -static AsApp * -fu_engine_filter_apps_by_requirements (FuEngine *self, GPtrArray *apps, - FuDevice *device, GError **error) +static gboolean +fu_engine_add_releases_for_device_component (FuEngine *self, + FuDevice *device, + XbNode *component, + GPtrArray *releases, + GError **error) { - g_autoptr(GError) error_all = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuInstallTask) task = fu_install_task_new (device, component); + g_autoptr(GPtrArray) releases_tmp = NULL; - /* find the first component that passes all the requirements */ - for (guint i = 0; i < apps->len; i++) { - AsApp *app_tmp = AS_APP (g_ptr_array_index (apps, i)); - g_autoptr(GError) error_local = NULL; - g_autoptr(FuInstallTask) task = fu_install_task_new (device, app_tmp); - if (!fu_engine_check_requirements (self, task, - FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | - FWUPD_INSTALL_FLAG_ALLOW_OLDER, - &error_local)) { - if (error_all == NULL) { - error_all = g_steal_pointer (&error_local); - continue; - } - /* assume the domain and code is the same */ - g_prefix_error (&error_all, "%s, ", error_local->message); + if (!fu_engine_check_requirements (self, task, + FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | + FWUPD_INSTALL_FLAG_ALLOW_OLDER, + error)) + return FALSE; + + /* get all releases */ + releases_tmp = xb_node_query (component, "releases/release", 0, &error_local); + if (releases_tmp == NULL) { + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + return TRUE; + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) + return TRUE; + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + for (guint i = 0; i < releases_tmp->len; i++) { + XbNode *release = g_ptr_array_index (releases_tmp, i); + const gchar *update_message; + GPtrArray *checksums; + g_autoptr(FwupdRelease) rel = fwupd_release_new (); + + /* create new FwupdRelease for the XbNode */ + fu_engine_set_release_from_appstream (self, rel, component, release); + + /* fall back to quirk-provided value */ + if (fwupd_release_get_install_duration (rel) == 0) + fwupd_release_set_install_duration (rel, fu_device_get_install_duration (device)); + + /* invalid */ + if (fwupd_release_get_uri (rel) == NULL) continue; + checksums = fwupd_release_get_checksums (rel); + if (checksums->len == 0) + continue; + + /* add update message if exists but device doesn't already have one */ + update_message = fwupd_release_get_update_message (rel); + if (fwupd_device_get_update_message (FWUPD_DEVICE (device)) == NULL && + update_message != NULL) { + fwupd_device_set_update_message (FWUPD_DEVICE (device), update_message); } - return g_object_ref (app_tmp); + /* success */ + g_ptr_array_add (releases, g_steal_pointer (&rel)); } - /* return the compound error */ - g_propagate_error (error, g_steal_pointer (&error_all)); - return NULL; + /* success */ + return TRUE; } static GPtrArray * @@ -2430,6 +2662,10 @@ fu_engine_get_releases_for_device (FuEngine *self, FuDevice *device, GError **er GPtrArray *device_guids; GPtrArray *releases; const gchar *version; + g_autoptr(GError) error_all = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(GString) xpath = g_string_new (NULL); /* get device version */ version = fu_device_get_version (device); @@ -2452,46 +2688,61 @@ fu_engine_get_releases_for_device (FuEngine *self, FuDevice *device, GError **er return NULL; } - releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + /* get all the components that provide any of these GUIDs */ device_guids = fu_device_get_guids (device); for (guint i = 0; i < device_guids->len; i++) { - GPtrArray *releases_tmp; - g_autoptr(AsApp) app = NULL; - g_autoptr(GPtrArray) apps = NULL; const gchar *guid = g_ptr_array_index (device_guids, i); - - /* get all the components that provide this GUID */ - apps = _as_store_get_apps_by_provide (self->store, - AS_PROVIDE_KIND_FIRMWARE_FLASHED, - guid); - if (apps->len == 0) - continue; - - /* filter by requirements */ - app = fu_engine_filter_apps_by_requirements (self, apps, device, error); - if (app == NULL) + xb_string_append_union (xpath, + "components/component/" + "provides/firmware[@type=$'flashed'][text()=$'%s']/" + "../..", guid); + } + components = xb_silo_query (self->silo, xpath->str, 0, &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)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No releases for %s", + fu_device_get_name (device)); return NULL; - - /* get all releases */ - releases_tmp = as_app_get_releases (app); - for (guint j = 0; j < releases_tmp->len; j++) { - AsRelease *release = g_ptr_array_index (releases_tmp, j); - GPtrArray *checksums; - g_autoptr(FwupdRelease) rel = fwupd_release_new (); - - /* create new FwupdRelease for the AsRelease */ - fu_engine_set_release_from_appstream (self, rel, app, release); - - /* invalid */ - if (fwupd_release_get_uri (rel) == NULL) - continue; - checksums = fwupd_release_get_checksums (rel); - if (checksums->len == 0) - continue; - - /* success */ - g_ptr_array_add (releases, g_steal_pointer (&rel)); } + g_propagate_error (error, g_steal_pointer (&error_local)); + return NULL; + } + + /* find all the releases that pass all the requirements */ + releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + for (guint i = 0; i < components->len; i++) { + XbNode *component = XB_NODE (g_ptr_array_index (components, i)); + g_autoptr(GError) error_tmp = NULL; + if (!fu_engine_add_releases_for_device_component (self, + device, + component, + releases, + &error_tmp)) { + if (error_all == NULL) { + error_all = g_steal_pointer (&error_tmp); + continue; + } + + /* assume the domain and code is the same */ + g_prefix_error (&error_all, "%s, ", error_tmp->message); + } + } + + /* return the compound error */ + if (releases->len == 0) { + if (error_all != NULL) { + g_propagate_error (error, g_steal_pointer (&error_all)); + return NULL; + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No valid releases found for device"); + return NULL; } return releases; } @@ -2573,7 +2824,7 @@ fu_engine_get_downgrades (FuEngine *self, const gchar *device_id, GError **error gint vercmp; /* only include older firmware */ - vercmp = as_utils_vercmp (fwupd_release_get_version (rel_tmp), + vercmp = fu_common_vercmp (fwupd_release_get_version (rel_tmp), fu_device_get_version (device)); if (vercmp == 0) { g_string_append_printf (error_str, "%s=same, ", @@ -2594,7 +2845,7 @@ fu_engine_get_downgrades (FuEngine *self, const gchar *device_id, GError **error /* don't show releases we are not allowed to dowgrade to */ if (fu_device_get_version_lowest (device) != NULL) { - if (as_utils_vercmp (fwupd_release_get_version (rel_tmp), + if (fu_common_vercmp (fwupd_release_get_version (rel_tmp), fu_device_get_version_lowest (device)) <= 0) { g_string_append_printf (error_str, "%s=lowest, ", fwupd_release_get_version (rel_tmp)); @@ -2676,7 +2927,7 @@ fu_engine_get_upgrades (FuEngine *self, const gchar *device_id, GError **error) gint vercmp; /* only include older firmware */ - vercmp = as_utils_vercmp (fwupd_release_get_version (rel_tmp), + vercmp = fu_common_vercmp (fwupd_release_get_version (rel_tmp), fu_device_get_version (device)); if (vercmp == 0) { g_string_append_printf (error_str, "%s=same, ", @@ -3014,12 +3265,15 @@ fu_engine_add_device (FuEngine *self, FuDevice *device) /* if this device is locked get some metadata from AppStream */ if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) { - AsApp *app = fu_engine_store_get_app_by_guids (self->store, device); - if (app != NULL) { - AsRelease *release = as_app_get_release_default (app); + g_autoptr(XbNode) component = fu_engine_get_component_by_guids (self, device); + if (component != NULL) { + g_autoptr(XbNode) release = NULL; + release = xb_node_query_first (component, + "releases/release", + NULL); if (release != NULL) { g_autoptr(FwupdRelease) rel = fwupd_release_new (); - fu_engine_set_release_from_appstream (self, rel, app, release); + fu_engine_set_release_from_appstream (self, rel, component, release); fu_device_add_release (device, rel); } } @@ -3042,13 +3296,24 @@ fu_engine_add_device (FuEngine *self, FuDevice *device) if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)) fu_engine_plugin_device_register (self, device); + /* create new device */ + fu_device_list_add (self->device_list, device); + /* match the metadata at this point so clients can tell if the * device is worthy */ if (fu_engine_is_device_supported (self, device)) fu_device_add_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED); +} - /* create new device */ - fu_device_list_add (self->device_list, device); +static void +fu_engine_plugin_rules_changed_cb (FuPlugin *plugin, gpointer user_data) +{ + FuEngine *self = FU_ENGINE (user_data); + GPtrArray *rules = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE); + for (guint j = 0; j < rules->len; j++) { + const gchar *tmp = g_ptr_array_index (rules, j); + fu_idle_inhibit (self->idle, tmp); + } } static void @@ -3250,10 +3515,22 @@ fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngi duration, self->coldplug_delay); } -/* for the self tests to use */ +/* this is called by the self tests as well */ void fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin) { + /* plugin does not match built version */ + if (fu_plugin_get_build_hash (plugin) == NULL) { + const gchar *name = fu_plugin_get_name (plugin); + g_warning ("%s should call fu_plugin_set_build_hash()", name); + self->tainted = TRUE; + } else if (g_strcmp0 (fu_plugin_get_build_hash (plugin), FU_BUILD_HASH) != 0) { + const gchar *name = fu_plugin_get_name (plugin); + g_warning ("%s has incorrect built version %s", + name, fu_plugin_get_build_hash (plugin)); + self->tainted = TRUE; + } + fu_plugin_list_add (self->plugin_list, plugin); } @@ -3290,6 +3567,24 @@ fu_engine_add_plugin_filter (FuEngine *self, const gchar *plugin_glob) g_ptr_array_add (self->plugin_filter, g_strdup (plugin_glob)); } +static gboolean +fu_engine_plugin_check_supported_cb (FuPlugin *plugin, const gchar *guid, FuEngine *self) +{ + g_autoptr(XbNode) n = NULL; + g_autofree gchar *xpath = NULL; + xpath = g_strdup_printf ("components/component/" + "provides/firmware[@type='flashed'][text()='%s']", + guid); + n = xb_silo_query_first (self->silo, xpath, NULL); + return n != NULL; +} + +gboolean +fu_engine_get_tainted (FuEngine *self) +{ + return self->tainted; +} + gboolean fu_engine_load_plugins (FuEngine *self, GError **error) { @@ -3332,7 +3627,6 @@ fu_engine_load_plugins (FuEngine *self, GError **error) fu_plugin_set_usb_context (plugin, self->usb_ctx); fu_plugin_set_hwids (plugin, self->hwids); fu_plugin_set_smbios (plugin, self->smbios); - fu_plugin_set_supported (plugin, self->supported_guids); fu_plugin_set_udev_subsystems (plugin, self->udev_subsystems); fu_plugin_set_quirks (plugin, self->quirks); fu_plugin_set_runtime_versions (plugin, self->runtime_versions); @@ -3371,9 +3665,15 @@ fu_engine_load_plugins (FuEngine *self, GError **error) g_signal_connect (plugin, "set-coldplug-delay", G_CALLBACK (fu_engine_plugin_set_coldplug_delay_cb), self); + g_signal_connect (plugin, "check-supported", + G_CALLBACK (fu_engine_plugin_check_supported_cb), + self); + g_signal_connect (plugin, "rules-changed", + G_CALLBACK (fu_engine_plugin_rules_changed_cb), + self); /* add */ - fu_plugin_list_add (self->plugin_list, plugin); + fu_engine_add_plugin (self, plugin); } /* depsolve into the correct order */ @@ -3562,9 +3862,17 @@ fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError * /* the system is running with the new firmware version */ if (g_strcmp0 (fu_device_get_version (dev), fwupd_release_get_version (rel_history)) == 0) { + GPtrArray *checksums; g_debug ("installed version %s matching history %s", fu_device_get_version (dev), fwupd_release_get_version (rel_history)); + + /* copy over runtime checksums if set from probe() */ + checksums = fu_device_get_checksums (dev); + for (guint i = 0; i < checksums->len; i++) { + const gchar *csum = g_ptr_array_index (checksums, i); + fu_device_add_checksum (dev_history, csum); + } fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_SUCCESS); return fu_history_modify_device (self->history, dev_history, FU_HISTORY_FLAGS_MATCH_NEW_VERSION, @@ -3652,19 +3960,26 @@ fu_engine_load (FuEngine *self, GError **error) g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); 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) + return TRUE; + /* read config file */ if (!fu_config_load (self->config, error)) { g_prefix_error (error, "Failed to load config: "); return FALSE; } + /* set up idle exit */ + if ((self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) == 0) + fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (self->config)); + /* load quirks, SMBIOS and the hwids */ fu_engine_load_smbios (self); fu_engine_load_hwids (self); fu_engine_load_quirks (self); /* load AppStream metadata */ - as_store_add_filter (self->store, AS_APP_KIND_FIRMWARE); if (!fu_engine_load_metadata_store (self, error)) { g_prefix_error (error, "Failed to load AppStream data: "); return FALSE; @@ -3735,6 +4050,7 @@ fu_engine_load (FuEngine *self, GError **error) return FALSE; fu_engine_set_status (self, FWUPD_STATUS_IDLE); + self->loaded = TRUE; /* success */ return TRUE; @@ -3788,6 +4104,14 @@ fu_engine_add_runtime_version (FuEngine *self, g_strdup (version)); } +static void +fu_engine_idle_status_notify_cb (FuIdle *idle, GParamSpec *pspec, FuEngine *self) +{ + FwupdStatus status = fu_idle_get_status (idle); + if (status == FWUPD_STATUS_SHUTDOWN) + fu_engine_set_status (self, status); +} + static void fu_engine_init (FuEngine *self) { @@ -3797,22 +4121,22 @@ fu_engine_init (FuEngine *self) self->device_list = fu_device_list_new (); self->smbios = fu_smbios_new (); self->hwids = fu_hwids_new (); + self->idle = fu_idle_new (); self->quirks = fu_quirks_new (); self->history = fu_history_new (); self->plugin_list = fu_plugin_list_new (); - self->store = as_store_new (); self->plugin_filter = g_ptr_array_new_with_free_func (g_free); - self->supported_guids = g_ptr_array_new_with_free_func (g_free); self->udev_subsystems = g_ptr_array_new_with_free_func (g_free); self->runtime_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + g_signal_connect (self->idle, "notify::status", + G_CALLBACK (fu_engine_idle_status_notify_cb), self); + /* add some runtime versions of things the daemon depends on */ fu_engine_add_runtime_version (self, "org.freedesktop.fwupd", VERSION); fu_engine_add_runtime_version (self, "com.redhat.fwupdate", "12"); -#if AS_CHECK_VERSION(0,7,8) - fu_engine_add_runtime_version (self, "org.freedesktop.appstream-glib", as_version_string ()); -#endif + fu_engine_add_runtime_version (self, "org.freedesktop.appstream-glib", "0.7.14"); #if G_USB_CHECK_VERSION(0,3,1) fu_engine_add_runtime_version (self, "org.freedesktop.gusb", g_usb_version_string ()); #endif @@ -3823,12 +4147,6 @@ fu_engine_init (FuEngine *self) g_hash_table_insert (self->compile_versions, g_strdup ("org.freedesktop.fwupd"), g_strdup (VERSION)); - g_hash_table_insert (self->compile_versions, - g_strdup ("org.freedesktop.appstream-glib"), - g_strdup_printf ("%i.%i.%i", - AS_MAJOR_VERSION, - AS_MINOR_VERSION, - AS_MICRO_VERSION)); g_hash_table_insert (self->compile_versions, g_strdup ("org.freedesktop.gusb"), g_strdup_printf ("%i.%i.%i", @@ -3845,19 +4163,20 @@ fu_engine_finalize (GObject *obj) if (self->usb_ctx != NULL) g_object_unref (self->usb_ctx); + if (self->silo != NULL) + g_object_unref (self->silo); if (self->gudev_client != NULL) g_object_unref (self->gudev_client); if (self->coldplug_id != 0) g_source_remove (self->coldplug_id); + g_object_unref (self->idle); g_object_unref (self->config); g_object_unref (self->smbios); g_object_unref (self->quirks); g_object_unref (self->hwids); g_object_unref (self->history); - g_object_unref (self->store); g_object_unref (self->device_list); - g_ptr_array_unref (self->supported_guids); g_ptr_array_unref (self->plugin_filter); g_ptr_array_unref (self->udev_subsystems); g_hash_table_unref (self->runtime_versions); diff --git a/src/fu-engine.h b/src/fu-engine.h index f271ed680..05388fd7d 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -9,7 +9,7 @@ G_BEGIN_DECLS -#include +#include #include #include "fwupd-device.h" @@ -25,12 +25,14 @@ G_DECLARE_FINAL_TYPE (FuEngine, fu_engine, FU, ENGINE, GObject) FuEngine *fu_engine_new (FuAppFlags app_flags); void fu_engine_add_plugin_filter (FuEngine *self, const gchar *plugin_glob); +void fu_engine_idle_reset (FuEngine *self); gboolean fu_engine_load (FuEngine *self, GError **error); gboolean fu_engine_load_plugins (FuEngine *self, GError **error); +gboolean fu_engine_get_tainted (FuEngine *self); FwupdStatus fu_engine_get_status (FuEngine *self); -AsStore *fu_engine_get_store_from_blob (FuEngine *self, +XbSilo *fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, GError **error); guint64 fu_engine_get_archive_size_max (FuEngine *self); @@ -42,6 +44,9 @@ FuDevice *fu_engine_get_device (FuEngine *self, GError **error); GPtrArray *fu_engine_get_history (FuEngine *self, GError **error); +FwupdRemote *fu_engine_get_remote_by_id (FuEngine *self, + const gchar *remote_id, + GError **error); GPtrArray *fu_engine_get_remotes (FuEngine *self, GError **error); GPtrArray *fu_engine_get_releases (FuEngine *self, @@ -122,10 +127,10 @@ gboolean fu_engine_check_requirements (FuEngine *self, FuInstallTask *task, FwupdInstallFlags flags, GError **error); -gboolean fu_engine_load_metadata_from_file (FuEngine *self, - const gchar *path, - const gchar *remote_id, - GError **error); +void fu_engine_set_silo (FuEngine *self, + XbSilo *silo); +XbNode *fu_engine_get_component_by_guids (FuEngine *self, + FuDevice *device); G_END_DECLS diff --git a/src/fu-hash.py b/src/fu-hash.py new file mode 100644 index 000000000..11cb1d731 --- /dev/null +++ b/src/fu-hash.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +""" Builds a header for the plugins to include """ + +# pylint: disable=invalid-name,wrong-import-position,pointless-string-statement + +""" +SPDX-License-Identifier: LGPL-2.1+ +""" + +import sys +import hashlib + +def usage(return_code): + """ print usage and exit with the supplied return code """ + if return_code == 0: + out = sys.stdout + else: + out = sys.stderr + out.write("usage: fu-hash.py
") + sys.exit(return_code) + +if __name__ == '__main__': + if {'-?', '--help', '--usage'}.intersection(set(sys.argv)): + usage(0) + if len(sys.argv) != 3: + usage(1) + with open(sys.argv[1], 'rb') as f: + buf = f.read() + csum = hashlib.sha256(buf).hexdigest() + with open(sys.argv[2], 'w') as f2: + f2.write('#ifndef FU_BUILD_HASH\n') + f2.write('#define FU_BUILD_HASH "%s"\n' % csum) + f2.write('#endif /* FU_BUILD_HASH */\n') diff --git a/src/fu-history.c b/src/fu-history.c index d6b0a18bb..20709d853 100644 --- a/src/fu-history.c +++ b/src/fu-history.c @@ -113,6 +113,16 @@ fu_history_device_from_stmt (sqlite3_stmt *stmt) tmp = (const gchar *) sqlite3_column_text (stmt, 13); if (tmp != NULL) fu_device_set_version (device, tmp); + + /* checksum_device */ + tmp = (const gchar *) sqlite3_column_text (stmt, 14); + if (tmp != NULL) + fu_device_add_checksum (device, tmp); + + /* protocol */ + tmp = (const gchar *) sqlite3_column_text (stmt, 15); + if (tmp != NULL) + fwupd_release_set_protocol (release, tmp); return device; } @@ -147,7 +157,7 @@ fu_history_create_database (FuHistory *self, GError **error) "CREATE TABLE schema (" "created timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," "version INTEGER DEFAULT 0);" - "INSERT INTO schema (version) VALUES (2);" + "INSERT INTO schema (version) VALUES (4);" "CREATE TABLE history (" "device_id TEXT," "update_state INTEGER DEFAULT 0," @@ -162,7 +172,9 @@ fu_history_create_database (FuHistory *self, GError **error) "metadata TEXT DEFAULT NULL," "guid_default TEXT DEFAULT NULL," "version_old TEXT," - "version_new TEXT);" + "version_new TEXT," + "checksum_device TEXT DEFAULT NULL," + "protocol TEXT DEFAULT NULL);" "COMMIT;", NULL, NULL, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -193,7 +205,11 @@ fu_history_migrate_database_v1 (FuHistory *self, GError **error) /* migrate the old entries to the new table */ rc = sqlite3_exec (self->db, - "INSERT INTO history SELECT * FROM history_old;" + "INSERT INTO history SELECT " + "device_id, update_state, update_error, filename, " + "display_name, plugin, device_created, device_modified, " + "checksum, flags, metadata, guid_default, version_old, " + "version_new, NULL, NULL FROM history_old;" "DROP TABLE history_old;", NULL, NULL, NULL); if (rc != SQLITE_OK) { @@ -203,6 +219,57 @@ fu_history_migrate_database_v1 (FuHistory *self, GError **error) return TRUE; } +static gboolean +fu_history_migrate_database_v2 (FuHistory *self, GError **error) +{ + gint rc; + + /* rename the table to something out the way */ + rc = sqlite3_exec (self->db, + "ALTER TABLE history ADD COLUMN checksum_device TEXT DEFAULT NULL;" + "ALTER TABLE history ADD COLUMN protocol TEXT DEFAULT NULL;", + NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to alter database: %s", + sqlite3_errmsg (self->db)); + return FALSE; + } + + /* update version */ + rc = sqlite3_exec (self->db, "UPDATE schema SET version=4;", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to migrate database: %s", + sqlite3_errmsg (self->db)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_history_migrate_database_v3 (FuHistory *self, GError **error) +{ + gint rc; + + /* rename the table to something out the way */ + rc = sqlite3_exec (self->db, + "ALTER TABLE history ADD COLUMN protocol TEXT DEFAULT NULL;", + NULL, NULL, NULL); + if (rc != SQLITE_OK) + g_debug ("ignoring database error: %s", sqlite3_errmsg (self->db)); + + /* update version */ + rc = sqlite3_exec (self->db, "UPDATE schema SET version=4;", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to update schema version: %s", + sqlite3_errmsg (self->db)); + return FALSE; + } + return TRUE; +} + /* returns 0 if database is not initialised */ static guint fu_history_get_schema_version (FuHistory *self) @@ -286,6 +353,14 @@ fu_history_load (FuHistory *self, GError **error) g_debug ("migrating v%u database", schema_ver); if (!fu_history_migrate_database_v1 (self, error)) return FALSE; + } else if (schema_ver == 2) { + g_debug ("migrating v%u database", schema_ver); + if (!fu_history_migrate_database_v2 (self, error)) + return FALSE; + } else if (schema_ver == 3) { + g_debug ("migrating v%u database", schema_ver); + if (!fu_history_migrate_database_v3 (self, error)) + return FALSE; } return TRUE; @@ -344,6 +419,7 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, "UPDATE history SET " "update_state = ?1, " "update_error = ?2, " + "checksum_device = ?6, " "flags = ?3 " "WHERE device_id = ?4;", -1, &stmt, NULL); @@ -356,6 +432,7 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, "UPDATE history SET " "update_state = ?1, " "update_error = ?2, " + "checksum_device = ?6, " "flags = ?3 " "WHERE device_id = ?4 AND version_old = ?5;", -1, &stmt, NULL); @@ -368,6 +445,7 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, "UPDATE history SET " "update_state = ?1, " "update_error = ?2, " + "checksum_device = ?6, " "flags = ?3 " "WHERE device_id = ?4 AND version_new = ?5;", -1, &stmt, NULL); @@ -380,17 +458,21 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, sqlite3_errmsg (self->db)); return FALSE; } + sqlite3_bind_int (stmt, 1, fu_device_get_update_state (device)); sqlite3_bind_text (stmt, 2, fu_device_get_update_error (device), -1, SQLITE_STATIC); sqlite3_bind_int64 (stmt, 3, fu_history_get_device_flags_filtered (device)); sqlite3_bind_text (stmt, 4, fu_device_get_id (device), -1, SQLITE_STATIC); sqlite3_bind_text (stmt, 5, fu_device_get_version (device), -1, SQLITE_STATIC); + sqlite3_bind_text (stmt, 6, fwupd_checksum_get_by_kind (fu_device_get_checksums (device), + G_CHECKSUM_SHA1), -1, SQLITE_STATIC); return fu_history_stmt_exec (self, stmt, NULL, error); } gboolean fu_history_add_device (FuHistory *self, FuDevice *device, FwupdRelease *release, GError **error) { + const gchar *checksum_device; const gchar *checksum = NULL; gint rc; g_autofree gchar *metadata = NULL; @@ -416,6 +498,8 @@ fu_history_add_device (FuHistory *self, FuDevice *device, FwupdRelease *release, GPtrArray *checksums = fwupd_release_get_checksums (release); checksum = fwupd_checksum_get_by_kind (checksums, G_CHECKSUM_SHA1); } + checksum_device = fwupd_checksum_get_by_kind (fu_device_get_checksums (device), + G_CHECKSUM_SHA1); /* metadata is stored as a simple string */ metadata = _convert_hash_to_string (fwupd_release_get_metadata (release)); @@ -437,9 +521,11 @@ fu_history_add_device (FuHistory *self, FuDevice *device, FwupdRelease *release, "device_created," "device_modified," "version_old," - "version_new) " + "version_new," + "checksum_device," + "protocol) " "VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10," - "?11,?12,?13,?14)", -1, &stmt, NULL); + "?11,?12,?13,?14,?15,?16)", -1, &stmt, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Failed to prepare SQL: %s", @@ -460,6 +546,8 @@ fu_history_add_device (FuHistory *self, FuDevice *device, FwupdRelease *release, sqlite3_bind_int64 (stmt, 12, fu_device_get_modified (device)); sqlite3_bind_text (stmt, 13, fu_device_get_version (device), -1, SQLITE_STATIC); sqlite3_bind_text (stmt, 14, fwupd_release_get_version (release), -1, SQLITE_STATIC); + sqlite3_bind_text (stmt, 15, checksum_device, -1, SQLITE_STATIC); + sqlite3_bind_text (stmt, 16, fwupd_release_get_protocol (release), -1, SQLITE_STATIC); return fu_history_stmt_exec (self, stmt, NULL, error); } @@ -594,7 +682,9 @@ fu_history_get_device_by_id (FuHistory *self, const gchar *device_id, GError **e "update_state, " "update_error, " "version_new, " - "version_old FROM history WHERE " + "version_old, " + "checksum_device, " + "protocol FROM history WHERE " "device_id = ?1 LIMIT 1", -1, &stmt, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -650,7 +740,9 @@ fu_history_get_devices (FuHistory *self, GError **error) "update_state, " "update_error, " "version_new, " - "version_old FROM history " + "version_old, " + "checksum_device, " + "protocol FROM history " "ORDER BY device_modified ASC;", -1, &stmt, NULL); if (rc != SQLITE_OK) { diff --git a/src/fu-hwids.c b/src/fu-hwids.c index c436e2bdb..61777ae30 100644 --- a/src/fu-hwids.c +++ b/src/fu-hwids.c @@ -11,9 +11,9 @@ #include #include #include -#include #include "fu-common.h" +#include "fu-common-guid.h" #include "fu-hwids.h" #include "fwupd-error.h" @@ -97,10 +97,10 @@ fu_hwids_get_guid_for_str (const gchar *str, GError **error) data[i] = GUINT16_TO_LE(data[i]); /* convert to a GUID */ - return as_utils_guid_from_data (namespace_id, - (guint8*) data, - items_written * 2, - error); + return fu_common_guid_from_data (namespace_id, + (guint8*) data, + items_written * 2, + error); } /** @@ -394,7 +394,6 @@ fu_hwids_setup (FuHwids *self, FuSmbios *smbios, GError **error) for (guint i = 0; i < 15; i++) { g_autofree gchar *guid = NULL; g_autofree gchar *key = NULL; - g_autofree gchar *values = NULL; g_autoptr(GError) error_local = NULL; /* get the GUID and add to hash */ @@ -407,11 +406,7 @@ fu_hwids_setup (FuHwids *self, FuSmbios *smbios, GError **error) g_hash_table_insert (self->hash_guid, g_strdup (guid), GUINT_TO_POINTER (1)); - g_ptr_array_add (self->array_guids, g_strdup (guid)); - - /* show what makes up the GUID */ - values = fu_hwids_get_replace_values (self, key, NULL); - g_debug ("{%s} <- %s", guid, values); + g_ptr_array_add (self->array_guids, g_steal_pointer (&guid)); } return TRUE; diff --git a/src/fu-idle.c b/src/fu-idle.c new file mode 100644 index 000000000..17b03720a --- /dev/null +++ b/src/fu-idle.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuIdle" + +#include "config.h" + +#include + +#include "fu-idle.h" +#include "fu-mutex.h" + +static void fu_idle_finalize (GObject *obj); + +struct _FuIdle +{ + GObject parent_instance; + GPtrArray *items; /* of FuIdleItem */ + FuMutex *items_mutex; + guint idle_id; + guint timeout; + FwupdStatus status; +}; + +enum { + PROP_0, + PROP_STATUS, + PROP_LAST +}; + +static void +fu_idle_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuIdle *self = FU_IDLE (object); + switch (prop_id) { + case PROP_STATUS: + g_value_set_uint (value, self->status); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_idle_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +typedef struct { + gchar *reason; + guint32 token; +} FuIdleItem; + +G_DEFINE_TYPE (FuIdle, fu_idle, G_TYPE_OBJECT) + +FwupdStatus +fu_idle_get_status (FuIdle *self) +{ + g_return_val_if_fail (FU_IS_IDLE (self), FWUPD_STATUS_UNKNOWN); + return self->status; +} + +static void +fu_idle_set_status (FuIdle *self, FwupdStatus status) +{ + if (self->status == status) + return; + self->status = status; + g_debug ("status now %s", fwupd_status_to_string (status)); + g_object_notify (G_OBJECT (self), "status"); +} + +static gboolean +fu_idle_check_cb (gpointer user_data) +{ + FuIdle *self = FU_IDLE (user_data); + fu_idle_set_status (self, FWUPD_STATUS_SHUTDOWN); + return G_SOURCE_CONTINUE; +} + +static void +fu_idle_start (FuIdle *self) +{ + if (self->idle_id != 0) + return; + if (self->timeout == 0) + return; + self->idle_id = g_timeout_add_seconds (self->timeout, fu_idle_check_cb, self); +} + +static void +fu_idle_stop (FuIdle *self) +{ + if (self->idle_id == 0) + return; + g_source_remove (self->idle_id); + self->idle_id = 0; +} + +void +fu_idle_reset (FuIdle *self) +{ + g_return_if_fail (FU_IS_IDLE (self)); + fu_idle_stop (self); + if (self->items->len == 0) + fu_idle_start (self); +} + +void +fu_idle_uninhibit (FuIdle *self, guint32 token) +{ + g_autoptr(FuMutexLocker) locker = fu_mutex_write_locker_new (self->items_mutex); + + g_return_if_fail (FU_IS_IDLE (self)); + g_return_if_fail (token != 0); + g_return_if_fail (locker != NULL); + + for (guint i = 0; i < self->items->len; i++) { + FuIdleItem *item = g_ptr_array_index (self->items, i); + if (item->token == token) { + g_debug ("uninhibiting: %s", item->reason); + g_ptr_array_remove_index (self->items, i); + break; + } + } + fu_idle_reset (self); +} + +guint32 +fu_idle_inhibit (FuIdle *self, const gchar *reason) +{ + FuIdleItem *item; + g_autoptr(FuMutexLocker) locker = fu_mutex_write_locker_new (self->items_mutex); + + g_return_val_if_fail (FU_IS_IDLE (self), 0); + g_return_val_if_fail (reason != NULL, 0); + g_return_val_if_fail (locker != NULL, 0); + + g_debug ("inhibiting: %s", reason); + item = g_new0 (FuIdleItem, 1); + item->reason = g_strdup (reason); + item->token = g_random_int_range (1, G_MAXINT); + g_ptr_array_add (self->items, item); + fu_idle_reset (self); + return item->token; +} + +void +fu_idle_set_timeout (FuIdle *self, guint timeout) +{ + g_return_if_fail (FU_IS_IDLE (self)); + g_debug ("setting timeout to %us", timeout); + self->timeout = timeout; + fu_idle_reset (self); +} + +static void +fu_idle_item_free (FuIdleItem *item) +{ + g_free (item->reason); + g_free (item); +} + +FuIdleLocker * +fu_idle_locker_new (FuIdle *self, const gchar *reason) +{ + FuIdleLocker *locker = g_new0 (FuIdleLocker, 1); + locker->idle = g_object_ref (self); + locker->token = fu_idle_inhibit (self, reason); + return locker; +} + +void +fu_idle_locker_free (FuIdleLocker *locker) +{ + if (locker == NULL) + return; + fu_idle_uninhibit (locker->idle, locker->token); + g_object_unref (locker->idle); + g_free (locker); +} + +static void +fu_idle_class_init (FuIdleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->finalize = fu_idle_finalize; + object_class->get_property = fu_idle_get_property; + object_class->set_property = fu_idle_set_property; + + pspec = g_param_spec_uint ("status", NULL, NULL, + FWUPD_STATUS_UNKNOWN, + FWUPD_STATUS_LAST, + FWUPD_STATUS_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_STATUS, pspec); +} + +static void +fu_idle_init (FuIdle *self) +{ + self->status = FWUPD_STATUS_IDLE; + self->items = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_idle_item_free); + self->items_mutex = fu_mutex_new (G_OBJECT_TYPE_NAME(self), "items"); +} + +static void +fu_idle_finalize (GObject *obj) +{ + FuIdle *self = FU_IDLE (obj); + + fu_idle_stop (self); + g_ptr_array_unref (self->items); + g_object_unref (self->items_mutex); + + G_OBJECT_CLASS (fu_idle_parent_class)->finalize (obj); +} + +FuIdle * +fu_idle_new (void) +{ + FuIdle *self; + self = g_object_new (FU_TYPE_IDLE, NULL); + return FU_IDLE (self); +} diff --git a/src/fu-idle.h b/src/fu-idle.h new file mode 100644 index 000000000..f5e4d0eb6 --- /dev/null +++ b/src/fu-idle.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_IDLE_H +#define __FU_IDLE_H + +G_BEGIN_DECLS + +#include +#include + +#include "fu-device.h" + +#define FU_TYPE_IDLE (fu_idle_get_type ()) +G_DECLARE_FINAL_TYPE (FuIdle, fu_idle, FU, IDLE, GObject) + +FuIdle *fu_idle_new (void); +guint32 fu_idle_inhibit (FuIdle *self, + const gchar *reason); +void fu_idle_uninhibit (FuIdle *self, + guint32 token); +void fu_idle_set_timeout (FuIdle *self, + guint timeout); +void fu_idle_reset (FuIdle *self); +FwupdStatus fu_idle_get_status (FuIdle *self); + +typedef struct { + FuIdle *idle; + guint32 token; +} FuIdleLocker; + +FuIdleLocker *fu_idle_locker_new (FuIdle *self, + const gchar *reason); +void fu_idle_locker_free (FuIdleLocker *locker); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuIdleLocker, fu_idle_locker_free) + +G_END_DECLS + +#endif /* __FU_IDLE_H */ + diff --git a/src/fu-install-task.c b/src/fu-install-task.c index 4145eddbe..06787a53e 100644 --- a/src/fu-install-task.c +++ b/src/fu-install-task.c @@ -10,6 +10,7 @@ #include +#include "fu-common-version.h" #include "fu-device-private.h" #include "fu-install-task.h" #include "fu-keyring-utils.h" @@ -18,7 +19,7 @@ struct _FuInstallTask { GObject parent_instance; FuDevice *device; - AsApp *app; + XbNode *component; FwupdTrustFlags trust_flags; gboolean is_downgrade; }; @@ -41,18 +42,18 @@ fu_install_task_get_device (FuInstallTask *self) } /** - * fu_install_task_get_app: + * fu_install_task_get_component: * @self: A #FuInstallTask * * Gets the component for this task. * * Returns: (transfer none): the component **/ -AsApp * -fu_install_task_get_app (FuInstallTask *self) +XbNode * +fu_install_task_get_component (FuInstallTask *self) { g_return_val_if_fail (FU_IS_INSTALL_TASK (self), NULL); - return self->app; + return self->component; } /** @@ -108,25 +109,33 @@ fu_install_task_check_requirements (FuInstallTask *self, FwupdInstallFlags flags, GError **error) { - AsRelease *release; - GPtrArray *provides; const gchar *version; const gchar *version_release; const gchar *version_lowest; gboolean matches_guid = FALSE; gint vercmp; g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) provides = NULL; + g_autoptr(XbNode) release = NULL; g_return_val_if_fail (FU_IS_INSTALL_TASK (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* does this app provide a GUID the device has */ - provides = as_app_get_provides (self->app); + /* does this component provide a GUID the device has */ + provides = xb_node_query (self->component, + "provides/firmware[@type='flashed']", + 0, &error_local); + if (provides == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No supported devices found: %s", + error_local->message); + return FALSE; + } for (guint i = 0; i < provides->len; i++) { - AsProvide *provide = g_ptr_array_index (provides, i); - if (as_provide_get_kind (provide) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; - if (fu_device_has_guid (self->device, as_provide_get_value (provide))) { + XbNode *provide = g_ptr_array_index (provides, i); + if (fu_device_has_guid (self->device, xb_node_get_text (provide))) { matches_guid = TRUE; break; } @@ -186,7 +195,7 @@ fu_install_task_check_requirements (FuInstallTask *self, } /* get latest release */ - release = as_app_get_release_default (self->app); + release = xb_node_query_first (self->component, "releases/release", NULL); if (release == NULL) { g_set_error (error, FWUPD_ERROR, @@ -198,7 +207,7 @@ fu_install_task_check_requirements (FuInstallTask *self, } /* is this a downgrade or re-install */ - version_release = as_release_get_version (release); + version_release = xb_node_get_attr (release, "version"); if (version_release == NULL) { g_set_error_literal (error, FWUPD_ERROR, @@ -210,7 +219,7 @@ fu_install_task_check_requirements (FuInstallTask *self, /* compare to the lowest supported version, if it exists */ version_lowest = fu_device_get_version_lowest (self->device); if (version_lowest != NULL && - as_utils_vercmp (version_lowest, version) > 0 && + fu_common_vercmp (version_lowest, version) > 0 && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { g_set_error (error, FWUPD_ERROR, @@ -221,7 +230,7 @@ fu_install_task_check_requirements (FuInstallTask *self, } /* check semver */ - vercmp = as_utils_vercmp (version, version_release); + vercmp = fu_common_vercmp (version, version_release); if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { g_set_error (error, FWUPD_ERROR, @@ -293,7 +302,7 @@ fu_install_task_finalize (GObject *object) { FuInstallTask *self = FU_INSTALL_TASK (object); - g_object_unref (self->app); + g_object_unref (self->component); if (self->device != NULL) g_object_unref (self->device); @@ -331,18 +340,18 @@ fu_install_task_compare (FuInstallTask *task1, FuInstallTask *task2) /** * fu_install_task_new: * @device: A #FuDevice - * @app: a #AsApp + * @component: a #XbNode * * Creates a new install task that may or may not be valid. * * Returns: (transfer full): the #FuInstallTask **/ FuInstallTask * -fu_install_task_new (FuDevice *device, AsApp *app) +fu_install_task_new (FuDevice *device, XbNode *component) { FuInstallTask *self; self = g_object_new (FU_TYPE_TASK, NULL); - self->app = g_object_ref (app); + self->component = g_object_ref (component); if (device != NULL) self->device = g_object_ref (device); return FU_INSTALL_TASK (self); diff --git a/src/fu-install-task.h b/src/fu-install-task.h index d3bbe45ca..c57178a3d 100644 --- a/src/fu-install-task.h +++ b/src/fu-install-task.h @@ -8,7 +8,7 @@ #define __FU_INSTALL_TASK_H #include -#include +#include #include "fu-device.h" @@ -18,9 +18,9 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (FuInstallTask, fu_install_task, FU, INSTALL_TASK, GObject) FuInstallTask *fu_install_task_new (FuDevice *device, - AsApp *app); + XbNode *component); FuDevice *fu_install_task_get_device (FuInstallTask *self); -AsApp *fu_install_task_get_app (FuInstallTask *self); +XbNode *fu_install_task_get_component (FuInstallTask *self); FwupdTrustFlags fu_install_task_get_trust_flags (FuInstallTask *self); gboolean fu_install_task_get_is_downgrade (FuInstallTask *self); gboolean fu_install_task_check_requirements (FuInstallTask *self, diff --git a/src/fu-io-channel.c b/src/fu-io-channel.c new file mode 100644 index 000000000..ce9083b38 --- /dev/null +++ b/src/fu-io-channel.c @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuIOChannel" + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "fwupd-error.h" +#include "fu-common.h" +#include "fu-io-channel.h" + +struct _FuIOChannel { + GObject parent_instance; + gint fd; +}; + +G_DEFINE_TYPE (FuIOChannel, fu_io_channel, G_TYPE_OBJECT) + +/** + * fu_io_channel_unix_get_fd: + * @self: a #FuIOChannel + * + * Gets the file descriptor for the device. + * + * Returns: fd, or -1 for not open. + * + * Since: 1.2.2 + **/ +gint +fu_io_channel_unix_get_fd (FuIOChannel *self) +{ + g_return_val_if_fail (FU_IS_IO_CHANNEL (self), -1); + return self->fd; +} + +/** + * fu_io_channel_shutdown: + * @self: a #FuIOChannel + * @error: a #GError, or %NULL + * + * Closes the file descriptor for the device. + * + * Returns: %TRUE if all the FD was closed. + * + * Since: 1.2.2 + **/ +gboolean +fu_io_channel_shutdown (FuIOChannel *self, GError **error) +{ + g_return_val_if_fail (FU_IS_IO_CHANNEL (self), FALSE); + if (!g_close (self->fd, error)) + return FALSE; + self->fd = -1; + return TRUE; +} + +static gboolean +fu_io_channel_flush_input (FuIOChannel *self, GError **error) +{ + GPollFD poll = { + .fd = self->fd, + .events = G_IO_IN | G_IO_ERR, + }; + while (g_poll (&poll, 1, 0) > 0) { + gchar c; + gint r = read (self->fd, &c, 1); + if (r < 0 && errno != EINTR) + break; + } + return TRUE; +} + +/** + * fu_io_channel_write_bytes: + * @self: a #FuIOChannel + * @bytes: buffer to write + * @timeout_ms: timeout in ms + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: a #GError, or %NULL + * + * Writes bytes to the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: %TRUE if all the bytes was written + * + * Since: 1.2.2 + **/ +gboolean +fu_io_channel_write_bytes (FuIOChannel *self, + GBytes *bytes, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data (bytes, &bufsz); + return fu_io_channel_write_raw (self, buf, bufsz, timeout_ms, flags, error); +} + +/** + * fu_io_channel_write_raw: + * @self: a #FuIOChannel + * @data: buffer to write + * @datasz: size of @data + * @timeout_ms: timeout in ms + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: a #GError, or %NULL + * + * Writes bytes to the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: %TRUE if all the bytes was written + * + * Since: 1.2.2 + **/ +gboolean +fu_io_channel_write_raw (FuIOChannel *self, + const guint8 *data, + gsize datasz, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + gsize idx = 0; + + g_return_val_if_fail (FU_IS_IO_CHANNEL (self), FALSE); + + /* flush pending reads */ + if (flags & FU_IO_CHANNEL_FLAG_FLUSH_INPUT) { + if (!fu_io_channel_flush_input (self, error)) + return FALSE; + } + + /* blocking IO */ + if (flags & FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO) { + gssize wrote = write (self->fd, data, datasz); + if (wrote != (gssize) datasz) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write: " + "wrote %" G_GSSIZE_FORMAT " of %" G_GSIZE_FORMAT, + wrote, datasz); + return FALSE; + } + return TRUE; + } + + /* nonblocking IO */ + while (idx < datasz) { + gint rc; + GPollFD fds = { + .fd = self->fd, + .events = G_IO_OUT | G_IO_ERR, + }; + + /* wait for data to be allowed to write without blocking */ + rc = g_poll (&fds, 1, (gint) timeout_ms); + if (rc == 0) + break; + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to poll %i", + self->fd); + return FALSE; + } + + /* we can write data */ + if (fds.revents & G_IO_OUT) { + gssize len = write (self->fd, data + idx, datasz - idx); + if (len < 0) { + if (errno == EAGAIN) { + g_debug ("got EAGAIN, trying harder"); + continue; + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write %" G_GSIZE_FORMAT + " bytes to %i: %s" , + datasz, + self->fd, + strerror (errno)); + return FALSE; + } + if (flags & FU_IO_CHANNEL_FLAG_SINGLE_SHOT) + break; + idx += len; + } + } + + return TRUE; +} + +/** + * fu_io_channel_read_bytes: + * @self: a #FuIOChannel + * @max_size: maximum size of the returned blob, or -1 for no limit + * @timeout_ms: timeout in ms + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: a #GError, or %NULL + * + * Reads bytes from the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: a #GBytes, or %NULL for error + * + * Since: 1.2.2 + **/ +GBytes * +fu_io_channel_read_bytes (FuIOChannel *self, + gssize max_size, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + GPollFD fds = { + .fd = self->fd, + .events = G_IO_IN | G_IO_PRI | G_IO_ERR, + }; + g_autoptr(GString) str = g_string_new (NULL); + + g_return_val_if_fail (FU_IS_IO_CHANNEL (self), NULL); + + /* blocking IO */ + if (flags & FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO) { + guint8 buf[1024]; + gssize len = read (self->fd, buf, sizeof (buf)); + if (len < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to read %i: %s", self->fd, + strerror (errno)); + return NULL; + } + if (len > 0) + g_string_append_len (str, (gchar *) buf, len); + return g_bytes_new (str->str, str->len); + } + + /* nonblocking IO */ + while (TRUE) { + /* wait for data to appear */ + gint rc = g_poll (&fds, 1, (gint) timeout_ms); + if (rc == 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "timeout"); + return FALSE; + } + if (rc < 0) { + if (errno == EINTR) + continue; + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to poll %i", self->fd); + return NULL; + } + + /* we have data to read */ + if (fds.revents & G_IO_IN) { + guint8 buf[1024]; + gssize len = read (self->fd, buf, sizeof (buf)); + if (len < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) + continue; + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to read %i: %s", self->fd, + strerror (errno)); + return NULL; + } + if (len > 0) + g_string_append_len (str, (gchar *) buf, len); + + /* check maximum size */ + if (max_size > 0 && str->len >= (guint) max_size) + break; + if (flags & FU_IO_CHANNEL_FLAG_SINGLE_SHOT) + break; + continue; + } + if (fds.revents & G_IO_ERR) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "error condition"); + return NULL; + } + if (fds.revents & G_IO_HUP) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "connection hung up"); + return NULL; + } + if (fds.revents & G_IO_NVAL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "invalid request"); + return NULL; + } + } + + /* no data */ + if (str->len == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "no data received from device in %ums", + timeout_ms); + return NULL; + } + + /* return blob */ + return g_bytes_new (str->str, str->len); +} + +/** + * fu_io_channel_read_raw: + * @self: a #FuIOChannel + * @buf: buffer, or %NULL + * @bufsz: size of @buf + * @bytes_read: (out): data written to @buf, or %NULL + * @timeout_ms: timeout in ms + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: a #GError, or %NULL + * + * Reads bytes from the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: a #GBytes, or %NULL for error + * + * Since: 1.2.2 + **/ +gboolean +fu_io_channel_read_raw (FuIOChannel *self, + guint8 *buf, + gsize bufsz, + gsize *bytes_read, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + const guint8 *tmpbuf = NULL; + gsize bytes_read_tmp; + g_autoptr(GBytes) tmp = NULL; + + g_return_val_if_fail (FU_IS_IO_CHANNEL (self), FALSE); + + tmp = fu_io_channel_read_bytes (self, bufsz, timeout_ms, flags, error); + if (tmp == NULL) + return FALSE; + tmpbuf = g_bytes_get_data (tmp, &bytes_read_tmp); + if (tmpbuf != NULL) + memcpy (buf, tmpbuf, bytes_read_tmp); + if (bytes_read != NULL) + *bytes_read = bytes_read_tmp; + return TRUE; +} + +static void +fu_io_channel_finalize (GObject *object) +{ + FuIOChannel *self = FU_IO_CHANNEL (object); + if (self->fd != -1) + g_close (self->fd, NULL); + G_OBJECT_CLASS (fu_io_channel_parent_class)->finalize (object); +} + +static void +fu_io_channel_class_init (FuIOChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_io_channel_finalize; +} + +static void +fu_io_channel_init (FuIOChannel *self) +{ + self->fd = -1; +} + +/** + * fu_io_channel_unix_new: + * @fd: file descriptor + * + * Creates a new object to write and read from. + * + * Returns: a #FuIOChannel + **/ +FuIOChannel * +fu_io_channel_unix_new (gint fd) +{ + FuIOChannel *self; + self = g_object_new (FU_TYPE_IO_CHANNEL, NULL); + self->fd = fd; + return FU_IO_CHANNEL (self); +} + +/** + * fu_io_channel_new_file: + * @filename: device file + * @error: a #GError, or %NULL + * + * Creates a new object to write and read from. + * + * Returns: a #FuIOChannel + **/ +FuIOChannel * +fu_io_channel_new_file (const gchar *filename, GError **error) +{ + gint fd = g_open (filename, O_RDWR | O_NONBLOCK, S_IRWXU); + if (fd < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to open %s", filename); + return NULL; + } + return fu_io_channel_unix_new (fd); +} diff --git a/src/fu-io-channel.h b/src/fu-io-channel.h new file mode 100644 index 000000000..c8a7d0daa --- /dev/null +++ b/src/fu-io-channel.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_IO_CHANNEL_H +#define __FU_IO_CHANNEL_H + +#include + +G_BEGIN_DECLS + +#define FU_TYPE_IO_CHANNEL (fu_io_channel_get_type ()) + +G_DECLARE_FINAL_TYPE (FuIOChannel, fu_io_channel, FU, IO_CHANNEL, GObject) + +/** + * 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 */ + /*< private >*/ + FU_IO_CHANNEL_FLAG_LAST +} FuIOChannelFlags; + +FuIOChannel *fu_io_channel_unix_new (gint fd); +FuIOChannel *fu_io_channel_new_file (const gchar *filename, + GError **error); + +gint fu_io_channel_unix_get_fd (FuIOChannel *self); +gboolean fu_io_channel_shutdown (FuIOChannel *self, + GError **error); +gboolean fu_io_channel_write_raw (FuIOChannel *self, + const guint8 *data, + gsize datasz, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error); +gboolean fu_io_channel_read_raw (FuIOChannel *self, + guint8 *buf, + gsize bufsz, + gsize *bytes_read, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error); +gboolean fu_io_channel_write_bytes (FuIOChannel *self, + GBytes *bytes, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error); +GBytes *fu_io_channel_read_bytes (FuIOChannel *self, + gssize max_size, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error); + +G_END_DECLS + +#endif /* __FU_IO_CHANNEL_H */ diff --git a/src/fu-keyring-result.c b/src/fu-keyring-result.c index 5deeb6adb..42f024387 100644 --- a/src/fu-keyring-result.c +++ b/src/fu-keyring-result.c @@ -98,11 +98,15 @@ fu_keyring_result_class_init (FuKeyringResultClass *klass) pspec = g_param_spec_int64 ("timestamp", NULL, NULL, 0, G_MAXINT64, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_TIMESTAMP, pspec); pspec = g_param_spec_string ("authority", NULL, NULL, NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_AUTHORITY, pspec); } diff --git a/src/fu-keyring-utils.c b/src/fu-keyring-utils.c index 0c5a7f04a..12c8d0bd4 100644 --- a/src/fu-keyring-utils.c +++ b/src/fu-keyring-utils.c @@ -67,7 +67,7 @@ fu_keyring_create_for_kind (FwupdKeyringKind kind, GError **error) /** * fu_keyring_get_release_trust_flags: - * @release: A #AsRelease, e.g. %FWUPD_KEYRING_KIND_GPG + * @release: A #XbNode, e.g. %FWUPD_KEYRING_KIND_GPG * @trust_flags: A #FwupdTrustFlags, e.g. %FWUPD_TRUST_FLAG_PAYLOAD * @error: A #GError, or %NULL * @@ -76,16 +76,16 @@ fu_keyring_create_for_kind (FwupdKeyringKind kind, GError **error) * Returns: %TRUE if @trust_flags has been set **/ gboolean -fu_keyring_get_release_trust_flags (AsRelease *release, +fu_keyring_get_release_trust_flags (XbNode *release, FwupdTrustFlags *trust_flags, GError **error) { - AsChecksum *csum_tmp; FwupdKeyringKind keyring_kind = FWUPD_KEYRING_KIND_UNKNOWN; GBytes *blob_payload; GBytes *blob_signature; const gchar *fn; g_autofree gchar *pki_dir = NULL; + g_autofree gchar *release_key = NULL; g_autofree gchar *sysconfdir = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(FuKeyring) kr = NULL; @@ -100,28 +100,17 @@ fu_keyring_get_release_trust_flags (AsRelease *release, { FWUPD_KEYRING_KIND_NONE, NULL } }; - /* no filename? */ - csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); - if (csum_tmp == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no content checksum for release"); - return FALSE; - } - fn = as_checksum_get_filename (csum_tmp); - if (fn == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no filename"); - return FALSE; - } + /* custom filename specified */ + fn = xb_node_query_attr (release, "checksum[@target='content']", "filename", NULL); + if (fn == NULL) + fn = "filename.bin"; /* no signature == no trust */ for (guint i = 0; keyrings[i].ext != NULL; i++) { - g_autofree gchar *fn_tmp = g_strdup_printf ("%s.%s", fn, keyrings[i].ext); - blob_signature = as_release_get_blob (release, fn_tmp); + g_autofree gchar *fn_tmp = NULL; + fn_tmp = g_strdup_printf ("fwupd::ReleaseBlob(%s.%s)", + fn, keyrings[i].ext); + blob_signature = g_object_get_data (G_OBJECT (release), fn_tmp); if (blob_signature != NULL) { keyring_kind = keyrings[i].kind; break; @@ -133,7 +122,8 @@ fu_keyring_get_release_trust_flags (AsRelease *release, } /* get payload */ - blob_payload = as_release_get_blob (release, fn); + release_key = g_strdup_printf ("fwupd::ReleaseBlob(%s)", fn); + blob_payload = g_object_get_data (G_OBJECT (release), release_key); if (blob_payload == NULL) { g_set_error_literal (error, FWUPD_ERROR, @@ -145,6 +135,7 @@ fu_keyring_get_release_trust_flags (AsRelease *release, /* check we were installed correctly */ sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR); pki_dir = g_build_filename (sysconfdir, "pki", PACKAGE_NAME, NULL); +#if defined(ENABLE_PKCS7) || defined(ENABLE_PKCS7) if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) { g_set_error (error, FWUPD_ERROR, @@ -152,6 +143,7 @@ fu_keyring_get_release_trust_flags (AsRelease *release, "PKI directory %s not found", pki_dir); return FALSE; } +#endif /* verify against the system trusted keys */ kr = fu_keyring_create_for_kind (keyring_kind, error); diff --git a/src/fu-keyring-utils.h b/src/fu-keyring-utils.h index fa5ff34ca..43551b010 100644 --- a/src/fu-keyring-utils.h +++ b/src/fu-keyring-utils.h @@ -7,14 +7,14 @@ #ifndef __FU_KEYRING_UTILS_H__ #define __FU_KEYRING_UTILS_H__ -#include +#include #include "fu-keyring.h" #include "fwupd-enums.h" FuKeyring *fu_keyring_create_for_kind (FwupdKeyringKind kind, GError **error); -gboolean fu_keyring_get_release_trust_flags (AsRelease *release, +gboolean fu_keyring_get_release_trust_flags (XbNode *release, FwupdTrustFlags *trust_flags, GError **error); diff --git a/src/fu-main.c b/src/fu-main.c index 9cb9530f0..7cbcc696c 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -8,10 +8,11 @@ #include "config.h" -#include +#include #include #include #include +#include #include #include #include @@ -43,8 +44,23 @@ typedef struct { PolkitAuthority *authority; guint owner_id; FuEngine *engine; + gboolean update_in_progress; + gboolean pending_sigterm; } FuMainPrivate; +static gboolean +fu_main_sigterm_cb (gpointer user_data) +{ + FuMainPrivate *priv = (FuMainPrivate *) user_data; + if (!priv->update_in_progress) { + g_main_loop_quit (priv->loop); + return G_SOURCE_REMOVE; + } + g_warning ("Received SIGTERM during a firmware update, ignoring"); + priv->pending_sigterm = TRUE; + return G_SOURCE_CONTINUE; +} + static void fu_main_engine_changed_cb (FuEngine *engine, FuMainPrivate *priv) { @@ -166,6 +182,10 @@ fu_main_engine_status_changed_cb (FuEngine *engine, FuMainPrivate *priv) { fu_main_set_status (priv, status); + + /* engine has gone idle */ + if (status == FWUPD_STATUS_SHUTDOWN) + g_main_loop_quit (priv->loop); } static void @@ -282,6 +302,7 @@ typedef struct { gchar *remote_id; gchar *key; gchar *value; + XbSilo *silo; } FuMainAuthHelper; static void @@ -291,6 +312,8 @@ fu_main_auth_helper_free (FuMainAuthHelper *helper) g_bytes_unref (helper->blob_cab); if (helper->subject != NULL) g_object_unref (helper->subject); + if (helper->silo != NULL) + g_object_unref (helper->silo); if (helper->install_tasks != NULL) g_ptr_array_unref (helper->install_tasks); if (helper->action_ids != NULL) @@ -447,6 +470,7 @@ fu_main_authorize_install_queue (FuMainAuthHelper *helper_ref) FuMainPrivate *priv = helper_ref->priv; g_autoptr(FuMainAuthHelper) helper = helper_ref; g_autoptr(GError) error = NULL; + gboolean ret; /* still more things to to authenticate */ if (helper->action_ids->len > 0) { @@ -463,11 +487,16 @@ fu_main_authorize_install_queue (FuMainAuthHelper *helper_ref) } /* all authenticated, so install all the things */ - if (!fu_engine_install_tasks (helper->priv->engine, - helper->install_tasks, - helper->blob_cab, - helper->flags, - &error)) { + priv->update_in_progress = TRUE; + ret = fu_engine_install_tasks (helper->priv->engine, + helper->install_tasks, + helper->blob_cab, + helper->flags, + &error); + priv->update_in_progress = FALSE; + if (priv->pending_sigterm) + g_main_loop_quit (priv->loop); + if (!ret) { g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } @@ -546,9 +575,8 @@ static gboolean fu_main_install_with_helper (FuMainAuthHelper *helper_ref, GError **error) { FuMainPrivate *priv = helper_ref->priv; - GPtrArray *apps; - g_autoptr(AsStore) store = NULL; g_autoptr(FuMainAuthHelper) helper = helper_ref; + g_autoptr(GPtrArray) components = NULL; g_autoptr(GPtrArray) devices_possible = NULL; g_autoptr(GPtrArray) errors = NULL; @@ -563,20 +591,22 @@ fu_main_install_with_helper (FuMainAuthHelper *helper_ref, GError **error) return FALSE; } - /* parse store */ - store = fu_engine_get_store_from_blob (priv->engine, - helper->blob_cab, - error); - if (store == NULL) + /* parse silo */ + helper->silo = fu_engine_get_silo_from_blob (priv->engine, + helper->blob_cab, + error); + if (helper->silo == NULL) return FALSE; - /* for each component in the store */ - apps = as_store_get_apps (store); + /* for each component in the silo */ + components = xb_silo_query (helper->silo, "components/component", 0, error); + if (components == NULL) + return FALSE; helper->action_ids = g_ptr_array_new_with_free_func (g_free); helper->install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); /* do any devices pass the requirements */ for (guint j = 0; j < devices_possible->len; j++) { @@ -586,19 +616,22 @@ fu_main_install_with_helper (FuMainAuthHelper *helper_ref, GError **error) g_autoptr(GError) error_local = NULL; /* is this component valid for the device */ - task = fu_install_task_new (device, app); + task = fu_install_task_new (device, component); if (!fu_engine_check_requirements (priv->engine, task, helper->flags, &error_local)) { g_debug ("requirement on %s:%s failed: %s", fu_device_get_id (device), - as_app_get_id (app), + xb_node_query_text (component, "id", NULL), error_local->message); g_ptr_array_add (errors, g_steal_pointer (&error_local)); continue; } + /* if component should have an update message from CAB */ + fu_device_incorporate_from_component (device, component); + /* get the action IDs for the valid device */ action_id = fu_install_task_get_action_id (task); if (!g_ptr_array_find (helper->action_ids, action_id, NULL)) @@ -648,6 +681,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, GVariant *val = NULL; g_autoptr(GError) error = NULL; + /* activity */ + fu_engine_idle_reset (priv->engine); + if (g_strcmp0 (method_name, "GetDevices") == 0) { g_autoptr(GPtrArray) devices = NULL; g_debug ("Called %s()", method_name); @@ -1076,9 +1112,15 @@ fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender, { FuMainPrivate *priv = (FuMainPrivate *) user_data; + /* activity */ + fu_engine_idle_reset (priv->engine); + if (g_strcmp0 (property_name, "DaemonVersion") == 0) return g_variant_new_string (VERSION); + if (g_strcmp0 (property_name, "Tainted") == 0) + return g_variant_new_boolean (fu_engine_get_tainted (priv->engine)); + if (g_strcmp0 (property_name, "Status") == 0) return g_variant_new_uint32 (fu_engine_get_status (priv->engine)); @@ -1267,6 +1309,10 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + g_unix_signal_add_full (G_PRIORITY_DEFAULT, + SIGTERM, fu_main_sigterm_cb, + priv, NULL); + /* load introspection from file */ priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml", &error); @@ -1299,6 +1345,8 @@ main (int argc, char *argv[]) else if (timed_exit) g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop); + g_debug ("Started with locale %s", g_getenv ("LANG")); + /* wait */ g_message ("Daemon ready for requests"); g_main_loop_run (priv->loop); diff --git a/src/fu-plugin-private.h b/src/fu-plugin-private.h index 2ff7f9ab2..cb25b4976 100644 --- a/src/fu-plugin-private.h +++ b/src/fu-plugin-private.h @@ -20,8 +20,6 @@ void fu_plugin_set_usb_context (FuPlugin *self, GUsbContext *usb_ctx); void fu_plugin_set_hwids (FuPlugin *self, FuHwids *hwids); -void fu_plugin_set_supported (FuPlugin *self, - GPtrArray *supported_guids); void fu_plugin_set_udev_subsystems (FuPlugin *self, GPtrArray *udev_subsystems); void fu_plugin_set_quirks (FuPlugin *self, @@ -40,6 +38,7 @@ void fu_plugin_set_priority (FuPlugin *self, guint priority); void fu_plugin_set_name (FuPlugin *self, const gchar *name); +const gchar *fu_plugin_get_build_hash (FuPlugin *self); GPtrArray *fu_plugin_get_rules (FuPlugin *self, FuPluginRule rule); gboolean fu_plugin_has_rule (FuPlugin *self, diff --git a/src/fu-plugin-vfuncs.h b/src/fu-plugin-vfuncs.h index 52dc368e0..63289355f 100644 --- a/src/fu-plugin-vfuncs.h +++ b/src/fu-plugin-vfuncs.h @@ -9,6 +9,7 @@ #include "fu-plugin.h" #include "fu-device.h" +#include "fu-hash.h" G_BEGIN_DECLS diff --git a/src/fu-plugin.c b/src/fu-plugin.c index b5af82f4c..b403d9538 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -44,11 +44,11 @@ typedef struct { guint priority; GPtrArray *rules[FU_PLUGIN_RULE_LAST]; gchar *name; + gchar *build_hash; FuHwids *hwids; FuQuirks *quirks; GHashTable *runtime_versions; GHashTable *compile_versions; - GPtrArray *supported_guids; GPtrArray *udev_subsystems; FuSmbios *smbios; GHashTable *devices; /* platform_id:GObject */ @@ -61,8 +61,10 @@ enum { SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_REMOVED, SIGNAL_DEVICE_REGISTER, + SIGNAL_RULES_CHANGED, SIGNAL_RECOLDPLUG, SIGNAL_SET_COLDPLUG_DELAY, + SIGNAL_CHECK_SUPPORTED, SIGNAL_LAST }; @@ -131,6 +133,34 @@ fu_plugin_set_name (FuPlugin *self, const gchar *name) priv->name = g_strdup (name); } +/** + * fu_plugin_set_build_hash: + * @self: A #FuPlugin + * @build_hash: A checksum + * + * Sets the plugin build hash, typically a SHA256 checksum. All plugins must + * set the correct checksum to avoid the daemon being marked as tainted. + * + * Since: 1.2.4 + **/ +void +fu_plugin_set_build_hash (FuPlugin *self, const gchar *build_hash) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_PLUGIN (self)); + g_return_if_fail (build_hash != NULL); + g_free (priv->build_hash); + priv->build_hash = g_strdup (build_hash); +} + +const gchar * +fu_plugin_get_build_hash (FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); + return priv->build_hash; +} + /** * fu_plugin_cache_lookup: * @self: A #FuPlugin @@ -514,15 +544,9 @@ fu_plugin_get_hwids (FuPlugin *self) gboolean fu_plugin_check_supported (FuPlugin *self, const gchar *guid) { - FuPluginPrivate *priv = GET_PRIVATE (self); - if (priv->supported_guids == NULL) - return FALSE; - for (guint i = 0; i < priv->supported_guids->len; i++) { - const gchar *guid_tmp = g_ptr_array_index (priv->supported_guids, i); - if (g_strcmp0 (guid, guid_tmp) == 0) - return TRUE; - } - return FALSE; + gboolean retval = FALSE; + g_signal_emit (self, signals[SIGNAL_CHECK_SUPPORTED], 0, guid, &retval); + return retval; } /** @@ -596,15 +620,6 @@ fu_plugin_set_hwids (FuPlugin *self, FuHwids *hwids) g_set_object (&priv->hwids, hwids); } -void -fu_plugin_set_supported (FuPlugin *self, GPtrArray *supported_guids) -{ - FuPluginPrivate *priv = GET_PRIVATE (self); - if (priv->supported_guids != NULL) - g_ptr_array_unref (priv->supported_guids); - priv->supported_guids = g_ptr_array_ref (supported_guids); -} - void fu_plugin_set_udev_subsystems (FuPlugin *self, GPtrArray *udev_subsystems) { @@ -741,23 +756,6 @@ fu_plugin_lookup_quirk_by_id_as_uint64 (FuPlugin *self, const gchar *group, cons return fu_common_strtoull (fu_plugin_lookup_quirk_by_id (self, group, key)); } -/** - * fu_plugin_get_supported: - * @self: A #FuPlugin - * - * Gets all the device GUIDs supported by the daemon. - * - * Returns: (element-type utf8) (transfer none): GUIDs - * - * Since: 1.0.0 - **/ -GPtrArray * -fu_plugin_get_supported (FuPlugin *self) -{ - FuPluginPrivate *priv = GET_PRIVATE (self); - return priv->supported_guids; -} - void fu_plugin_set_smbios (FuPlugin *self, FuSmbios *smbios) { @@ -807,6 +805,7 @@ fu_plugin_runner_startup (FuPlugin *self, GError **error) { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -821,8 +820,18 @@ fu_plugin_runner_startup (FuPlugin *self, GError **error) if (func == NULL) return TRUE; g_debug ("performing startup() on %s", priv->name); - if (!func (self, error)) { - g_prefix_error (error, "failed to startup %s: ", priv->name); + if (!func (self, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for startup()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to startup using %s: ", + priv->name); return FALSE; } return TRUE; @@ -878,6 +887,7 @@ fu_plugin_runner_device_generic (FuPlugin *self, FuDevice *device, { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -892,10 +902,18 @@ fu_plugin_runner_device_generic (FuPlugin *self, FuDevice *device, if (func == NULL) return TRUE; g_debug ("performing %s() on %s", symbol_name + 10, priv->name); - if (!func (self, device, error)) { - g_prefix_error (error, "failed to run %s() on %s: ", - symbol_name + 10, - priv->name); + if (!func (self, device, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for %s()", + priv->name, symbol_name + 10); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to %s using %s: ", + symbol_name + 10, priv->name); return FALSE; } return TRUE; @@ -908,6 +926,7 @@ fu_plugin_runner_flagged_device_generic (FuPlugin *self, FwupdInstallFlags flags { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginFlaggedDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -922,10 +941,18 @@ fu_plugin_runner_flagged_device_generic (FuPlugin *self, FwupdInstallFlags flags if (func == NULL) return TRUE; g_debug ("performing %s() on %s", symbol_name + 10, priv->name); - if (!func (self, flags, device, error)) { - g_prefix_error (error, "failed to run %s() on %s: ", - symbol_name + 10, - priv->name); + if (!func (self, flags, device, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for %s()", + priv->name, symbol_name + 10); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to %s using %s: ", + symbol_name + 10, priv->name); return FALSE; } return TRUE; @@ -938,6 +965,7 @@ fu_plugin_runner_device_array_generic (FuPlugin *self, GPtrArray *devices, { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceArrayFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -952,10 +980,18 @@ fu_plugin_runner_device_array_generic (FuPlugin *self, GPtrArray *devices, if (func == NULL) return TRUE; g_debug ("performing %s() on %s", symbol_name + 10, priv->name); - if (!func (self, devices, error)) { - g_prefix_error (error, "failed to run %s() on %s: ", - symbol_name + 10, - priv->name); + if (!func (self, devices, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for %s()", + priv->name, symbol_name + 10); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to %s using %s: ", + symbol_name + 10, priv->name); return FALSE; } return TRUE; @@ -966,6 +1002,7 @@ fu_plugin_runner_coldplug (FuPlugin *self, GError **error) { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -980,8 +1017,17 @@ fu_plugin_runner_coldplug (FuPlugin *self, GError **error) if (func == NULL) return TRUE; g_debug ("performing coldplug() on %s", priv->name); - if (!func (self, error)) { - g_prefix_error (error, "failed to coldplug %s: ", priv->name); + if (!func (self, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for coldplug()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to coldplug using %s: ", priv->name); return FALSE; } return TRUE; @@ -992,6 +1038,7 @@ fu_plugin_runner_recoldplug (FuPlugin *self, GError **error) { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1006,8 +1053,18 @@ fu_plugin_runner_recoldplug (FuPlugin *self, GError **error) if (func == NULL) return TRUE; g_debug ("performing recoldplug() on %s", priv->name); - if (!func (self, error)) { - g_prefix_error (error, "failed to recoldplug %s: ", priv->name); + if (!func (self, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for recoldplug()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to recoldplug using %s: ", + priv->name); return FALSE; } return TRUE; @@ -1018,6 +1075,7 @@ fu_plugin_runner_coldplug_prepare (FuPlugin *self, GError **error) { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1032,8 +1090,18 @@ fu_plugin_runner_coldplug_prepare (FuPlugin *self, GError **error) if (func == NULL) return TRUE; g_debug ("performing coldplug_prepare() on %s", priv->name); - if (!func (self, error)) { - g_prefix_error (error, "failed to prepare for coldplug %s: ", priv->name); + if (!func (self, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for coldplug_prepare()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to coldplug_prepare using %s: ", + priv->name); return FALSE; } return TRUE; @@ -1044,6 +1112,7 @@ fu_plugin_runner_coldplug_cleanup (FuPlugin *self, GError **error) { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1058,8 +1127,18 @@ fu_plugin_runner_coldplug_cleanup (FuPlugin *self, GError **error) if (func == NULL) return TRUE; g_debug ("performing coldplug_cleanup() on %s", priv->name); - if (!func (self, error)) { - g_prefix_error (error, "failed to cleanup coldplug %s: ", priv->name); + if (!func (self, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for coldplug_cleanup()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to coldplug_cleanup using %s: ", + priv->name); return FALSE; } return TRUE; @@ -1147,6 +1226,7 @@ fu_plugin_runner_usb_device_added (FuPlugin *self, FuUsbDevice *device, GError * { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginUsbDeviceAddedFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1158,9 +1238,22 @@ fu_plugin_runner_usb_device_added (FuPlugin *self, FuUsbDevice *device, GError * /* optional */ g_module_symbol (priv->module, "fu_plugin_usb_device_added", (gpointer *) &func); - if (func != NULL) { - g_debug ("performing usb_device_added() on %s", priv->name); - return func (self, device, error); + if (func == NULL) + return TRUE; + g_debug ("performing usb_device_added() on %s", priv->name); + if (!func (self, device, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for usb_device_added()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to add device using on %s: ", + priv->name); + return FALSE; } return TRUE; } @@ -1170,6 +1263,7 @@ fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginUdevDeviceAddedFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1181,9 +1275,22 @@ fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError /* optional */ g_module_symbol (priv->module, "fu_plugin_udev_device_added", (gpointer *) &func); - if (func != NULL) { - g_debug ("performing udev_device_added() on %s", priv->name); - return func (self, device, error); + if (func == NULL) + return TRUE; + g_debug ("performing udev_device_added() on %s", priv->name); + if (!func (self, device, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for udev_device_added()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to add device using on %s: ", + priv->name); + return FALSE; } return TRUE; } @@ -1296,6 +1403,7 @@ fu_plugin_runner_verify (FuPlugin *self, FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginVerifyFunc func = NULL; GPtrArray *checksums; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1305,17 +1413,29 @@ fu_plugin_runner_verify (FuPlugin *self, if (priv->module == NULL) return TRUE; - /* clear any existing verification checksums */ - checksums = fu_device_get_checksums (device); - g_ptr_array_set_size (checksums, 0); - /* optional */ g_module_symbol (priv->module, "fu_plugin_verify", (gpointer *) &func); if (func == NULL) return TRUE; + + /* clear any existing verification checksums */ + checksums = fu_device_get_checksums (device); + g_ptr_array_set_size (checksums, 0); + + /* run vfunc */ g_debug ("performing verify() on %s", priv->name); - if (!func (self, device, flags, error)) { - g_prefix_error (error, "failed to verify %s: ", priv->name); + if (!func (self, device, flags, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for verify()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to verify using %s: ", + priv->name); return FALSE; } return TRUE; @@ -1361,8 +1481,7 @@ fu_plugin_runner_update (FuPlugin *self, FuPluginUpdateFunc update_func; g_autoptr(FuHistory) history = NULL; g_autoptr(FuDevice) device_pending = NULL; - GError *error_update = NULL; - GPtrArray *checksums; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) { @@ -1408,24 +1527,27 @@ fu_plugin_runner_update (FuPlugin *self, /* online */ history = fu_history_new (); device_pending = fu_history_get_device_by_id (history, fu_device_get_id (device), NULL); - if (!update_func (self, device, blob_fw, flags, &error_update)) { - if (error_update == NULL) { - g_critical ("plugin %s returned FALSE from UpdateFunc " - "but did not set error!", - fu_plugin_get_name (self)); - g_set_error_literal (&error_update, + if (!update_func (self, device, blob_fw, flags, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for update()", + priv->name); + g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + return FALSE; } - fu_device_set_update_error (device, error_update->message); - g_propagate_error (error, error_update); + fu_device_set_update_error (device, error_local->message); + g_propagate_error (error, g_steal_pointer (&error_local)); return FALSE; } /* no longer valid */ - checksums = fu_device_get_checksums (device); - g_ptr_array_set_size (checksums, 0); + if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT) && + !fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) { + GPtrArray *checksums = fu_device_get_checksums (device); + g_ptr_array_set_size (checksums, 0); + } /* cleanup */ if (device_pending != NULL) { @@ -1443,15 +1565,15 @@ fu_plugin_runner_update (FuPlugin *self, release = fu_device_get_release_default (device_pending); tmp = fwupd_release_get_filename (release); if (tmp != NULL && g_str_has_prefix (tmp, LIBEXECDIR)) { - g_autoptr(GError) error_local = NULL; + g_autoptr(GError) error_delete = NULL; g_autoptr(GFile) file = NULL; file = g_file_new_for_path (tmp); - if (!g_file_delete (file, NULL, &error_local)) { + if (!g_file_delete (file, NULL, &error_delete)) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Failed to delete %s: %s", - tmp, error_local->message); + tmp, error_delete->message); return FALSE; } } @@ -1464,6 +1586,7 @@ fu_plugin_runner_clear_results (FuPlugin *self, FuDevice *device, GError **error { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1478,8 +1601,18 @@ fu_plugin_runner_clear_results (FuPlugin *self, FuDevice *device, GError **error if (func == NULL) return TRUE; g_debug ("performing clear_result() on %s", priv->name); - if (!func (self, device, error)) { - g_prefix_error (error, "failed to clear_result %s: ", priv->name); + if (!func (self, device, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for clear_result()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to clear_result using %s: ", + priv->name); return FALSE; } return TRUE; @@ -1490,6 +1623,7 @@ fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error) { FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1504,8 +1638,18 @@ fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error) if (func == NULL) return TRUE; g_debug ("performing get_results() on %s", priv->name); - if (!func (self, device, error)) { - g_prefix_error (error, "failed to get_results %s: ", priv->name); + if (!func (self, device, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for get_results()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to get_results using %s: ", + priv->name); return FALSE; } return TRUE; @@ -1589,6 +1733,7 @@ fu_plugin_add_rule (FuPlugin *self, FuPluginRule rule, const gchar *name) { FuPluginPrivate *priv = fu_plugin_get_instance_private (self); g_ptr_array_add (priv->rules[rule], g_strdup (name)); + g_signal_emit (self, signals[SIGNAL_RULES_CHANGED], 0); } /** @@ -1767,6 +1912,18 @@ fu_plugin_class_init (FuPluginClass *klass) G_STRUCT_OFFSET (FuPluginClass, set_coldplug_delay), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); + signals[SIGNAL_CHECK_SUPPORTED] = + g_signal_new ("check-supported", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FuPluginClass, check_supported), + NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, 1, G_TYPE_STRING); + signals[SIGNAL_RULES_CHANGED] = + g_signal_new ("rules-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FuPluginClass, rules_changed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -1807,8 +1964,6 @@ fu_plugin_finalize (GObject *object) g_object_unref (priv->hwids); if (priv->quirks != NULL) g_object_unref (priv->quirks); - if (priv->supported_guids != NULL) - g_ptr_array_unref (priv->supported_guids); if (priv->udev_subsystems != NULL) g_ptr_array_unref (priv->udev_subsystems); if (priv->smbios != NULL) diff --git a/src/fu-plugin.h b/src/fu-plugin.h index 4177f8197..bc738951c 100644 --- a/src/fu-plugin.h +++ b/src/fu-plugin.h @@ -7,12 +7,13 @@ #ifndef __FU_PLUGIN_H #define __FU_PLUGIN_H -#include #include #include #include #include "fu-common.h" +#include "fu-common-guid.h" +#include "fu-common-version.h" #include "fu-device.h" #include "fu-device-locker.h" #include "fu-quirks.h" @@ -42,8 +43,11 @@ struct _FuPluginClass guint duration); void (* device_register) (FuPlugin *self, FuDevice *device); + gboolean (* check_supported) (FuPlugin *self, + const gchar *guid); + void (* rules_changed) (FuPlugin *self); /*< private >*/ - gpointer padding[24]; + gpointer padding[22]; }; /** @@ -65,6 +69,8 @@ typedef enum { * @FU_PLUGIN_RULE_RUN_BEFORE: Order the plugin before another * @FU_PLUGIN_RULE_REQUIRES_QUIRK: Requires a specific quirk * @FU_PLUGIN_RULE_BETTER_THAN: Is better than another plugin + * @FU_PLUGIN_RULE_INHIBITS_IDLE: The plugin inhibits the idle shutdown + * @FU_PLUGIN_RULE_SUPPORTS_PROTOCOL: The plugin supports a well known protocol * * The rules used for ordering plugins. * Plugins are expected to add rules in fu_plugin_initialize(). @@ -75,6 +81,8 @@ typedef enum { FU_PLUGIN_RULE_RUN_BEFORE, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_PLUGIN_RULE_BETTER_THAN, + FU_PLUGIN_RULE_INHIBITS_IDLE, + FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, /*< private >*/ FU_PLUGIN_RULE_LAST } FuPluginRule; @@ -89,8 +97,9 @@ FuPluginData *fu_plugin_alloc_data (FuPlugin *self, gboolean fu_plugin_get_enabled (FuPlugin *self); void fu_plugin_set_enabled (FuPlugin *self, gboolean enabled); +void fu_plugin_set_build_hash (FuPlugin *self, + const gchar *build_hash); GUsbContext *fu_plugin_get_usb_context (FuPlugin *self); -GPtrArray *fu_plugin_get_supported (FuPlugin *self); void fu_plugin_device_add (FuPlugin *self, FuDevice *device); void fu_plugin_device_remove (FuPlugin *self, diff --git a/src/fu-progressbar.c b/src/fu-progressbar.c index d0d602912..eaf0009f4 100644 --- a/src/fu-progressbar.c +++ b/src/fu-progressbar.c @@ -29,6 +29,7 @@ struct _FuProgressbar gint64 last_animated; /* monotonic */ GTimer *time_elapsed; gdouble last_estimate; + gboolean interactive; }; G_DEFINE_TYPE (FuProgressbar, fu_progressbar, G_TYPE_OBJECT) @@ -157,7 +158,7 @@ fu_progressbar_refresh (FuProgressbar *self, FwupdStatus status, guint percentag } title = fu_progressbar_status_to_string (status); g_string_append (str, title); - for (i = str->len; i < self->length_status; i++) + for (i = g_utf8_strlen (str->str, -1); i < self->length_status; i++) g_string_append_c (str, ' '); /* add progressbar */ @@ -186,7 +187,7 @@ fu_progressbar_refresh (FuProgressbar *self, FwupdStatus status, guint percentag /* dump to screen */ g_print ("%s", str->str); - self->to_erase = str->len - 2; + self->to_erase = str->len; /* done */ if (is_idle_newline) { @@ -271,6 +272,14 @@ fu_progressbar_update (FuProgressbar *self, FwupdStatus status, guint percentage if (status == FWUPD_STATUS_UNKNOWN) status = self->status; + if (!self->interactive) { + if (self->status != status) { + g_print ("%s\n", fu_progressbar_status_to_string (status)); + self->status = status; + } + return; + } + /* if the main loop isn't spinning and we've not had a chance to * execute the callback just do the refresh now manually */ if (percentage == 0 && @@ -302,6 +311,13 @@ fu_progressbar_update (FuProgressbar *self, FwupdStatus status, guint percentage self->percentage = percentage; } +void +fu_progressbar_set_interactive (FuProgressbar *self, gboolean interactive) +{ + g_return_if_fail (FU_IS_PROGRESSBAR (self)); + self->interactive = interactive; +} + void fu_progressbar_set_length_status (FuProgressbar *self, guint len) { @@ -332,6 +348,7 @@ fu_progressbar_init (FuProgressbar *self) self->length_status = 25; self->spinner_count_up = TRUE; self->time_elapsed = g_timer_new (); + self->interactive = TRUE; } static void diff --git a/src/fu-progressbar.h b/src/fu-progressbar.h index e48c35434..589b9f58b 100644 --- a/src/fu-progressbar.h +++ b/src/fu-progressbar.h @@ -26,6 +26,8 @@ void fu_progressbar_set_length_percentage (FuProgressbar *self, guint len); void fu_progressbar_set_title (FuProgressbar *self, const gchar *title); +void fu_progressbar_set_interactive (FuProgressbar *self, + gboolean interactive); G_END_DECLS diff --git a/src/fu-quirks.c b/src/fu-quirks.c index 2f6ead814..c41845d30 100644 --- a/src/fu-quirks.c +++ b/src/fu-quirks.c @@ -11,9 +11,9 @@ #include #include #include -#include #include "fu-common.h" +#include "fu-common-guid.h" #include "fu-mutex.h" #include "fu-quirks.h" @@ -97,9 +97,9 @@ fu_quirks_build_group_key (const gchar *group) for (guint i = 0; guid_prefixes[i] != NULL; i++) { if (g_str_has_prefix (group, guid_prefixes[i])) { gsize len = strlen (guid_prefixes[i]); - if (as_utils_guid_is_valid (group + len)) + if (fu_common_guid_is_valid (group + len)) return g_strdup (group + len); - return as_utils_guid_from_string (group + len); + return fu_common_guid_from_string (group + len); } } diff --git a/src/fu-quirks.h b/src/fu-quirks.h index c677de157..41a7f7a18 100644 --- a/src/fu-quirks.h +++ b/src/fu-quirks.h @@ -29,218 +29,24 @@ gboolean fu_quirks_get_kvs_for_guid (FuQuirks *self, const gchar *guid, GHashTableIter *iter); -/** - * FU_QUIRKS_PLUGIN: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the plugin name, e.g. `csr` - * - * Sets the plugin to use for a specific hardware device. - * - * Since: 1.1.0 - */ #define FU_QUIRKS_PLUGIN "Plugin" - -/** - * FU_QUIRKS_UEFI_VERSION_FORMAT: - * @key: a %FU_HWIDS_KEY_MANUFACTURER, e.g. `Alienware` - * @value: the version format, e.g. `none` - * - * Assigns the version format to use for a specific manufacturer. - * A specific version format is sometimes chosen to match the appearance of - * other systems or specifications. - * - * Default value: `use-triplet` - * - * Since: 1.0.1 - */ #define FU_QUIRKS_UEFI_VERSION_FORMAT "UefiVersionFormat" - -/** - * FU_QUIRKS_DAEMON_VERSION_FORMAT: - * @key: the optionally wildcarded AppStream ID e.g. `com.dell.uefi*.firmware` - * @value: the version format, e.g. `none` - * - * Assigns the version format to use for a specific AppStream component. - * A specific version format is sometimes chosen to match the appearance of - * other systems or specifications. - * - * Default value: `use-triplet` - * - * Since: 1.0.1 - */ #define FU_QUIRKS_DAEMON_VERSION_FORMAT "ComponentIDs" - -/** - * FU_QUIRKS_FLAGS: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the quirk, e.g. `is-bootloader` - * - * Assigns optional quirks to use for a 8Bitdo device. The list of supported - * quirks is thus: - * - * * `none`: No device quirks - * * `is-bootloader`: Device is in bootloader mode - * - * Since: 1.0.3 - */ #define FU_QUIRKS_FLAGS "Flags" - -/** - * FU_QUIRKS_SUMMARY: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the USB device summary, e.g. `An open source display colorimeter` - * - * Sets a name for a specific hardware device. - * - * Since: 1.0.2 - */ #define FU_QUIRKS_SUMMARY "Summary" - -/** - * FU_QUIRKS_ICON: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the USB device icon name, e.g. `media-removable` - * - * Adds an icon name for a specific hardware device. - * - * Since: 1.0.2 - */ #define FU_QUIRKS_ICON "Icon" - -/** - * FU_QUIRKS_NAME: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the USB device name, e.g. `ColorHug` - * - * Sets a name for a specific hardware device. - * - * Since: 1.0.2 - */ #define FU_QUIRKS_NAME "Name" - -/** - * FU_QUIRKS_GUID: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the GUID, e.g. `537f7800-8529-5656-b2fa-b0901fe91696` - * - * Adds an extra GUID for a specific hardware device. If the value provided is - * not already a suitable GUID, it will be converted to one. - * - * Since: 1.0.3 - */ #define FU_QUIRKS_GUID "Guid" - -/** - * FU_QUIRKS_COUNTERPART_GUID: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the GUID, e.g. `537f7800-8529-5656-b2fa-b0901fe91696` - * - * Adds an counterpart GUID for a specific hardware device. If the value - * provided is not already a suitable GUID, it will be converted to one. - * - * A counterpart GUID is typically the GUID of the same device in bootloader - * or runtime mode, if they have a different device PCI or USB ID. Adding this - * type of GUID does not cause a "cascade" by matching using the quirk database. - * - * Since: 1.1.2 - */ #define FU_QUIRKS_COUNTERPART_GUID "CounterpartGuid" - -/** - * FU_QUIRKS_PARENT_GUID: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the GUID, e.g. `537f7800-8529-5656-b2fa-b0901fe91696` - * - * Adds an extra GUID to mark as the parent device. If the value provided is - * not already a suitable GUID, it will be converted to one. - * - * Since: 1.1.2 - */ #define FU_QUIRKS_PARENT_GUID "ParentGuid" - -/** - * FU_QUIRKS_CHILDREN: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the GUID, e.g. `USB\VID_0763&PID_2806&I2C_01` - * - * Adds one or more virtual devices to a physical device, delimited by comma. - * - * To set the object type of the child device use a pipe before the object type, - * for instance: `FuRts54xxDevice|USB\VID_0763&PID_2806&I2C_01` - * If the type of device is not specified the parent device type is used. - * - * If the values provided are not already suitable GUIDs, they will be - * converted. - * - * Since: 1.1.2 - */ #define FU_QUIRKS_CHILDREN "Children" - -/** - * FU_QUIRKS_VERSION: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806&REV_0001` - * @value: the version number, e.g. `1.2` - * - * Sets a version for a specific hardware device. - * - * Since: 1.0.3 - */ #define FU_QUIRKS_VERSION "Version" - -/** - * FU_QUIRKS_VENDOR: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the vendor, e.g. `Hughski Limited` - * - * Sets a vendor name for a specific hardware device. - * - * Since: 1.0.3 - */ #define FU_QUIRKS_VENDOR "Vendor" - -/** - * FU_QUIRKS_VENDOR_ID: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the vendor, e.g. `USB:0x123A` - * - * Sets a vendor ID for a specific hardware device. - * - * Since: 1.1.2 - */ #define FU_QUIRKS_VENDOR_ID "VendorId" - -/** - * FU_QUIRKS_FIRMWARE_SIZE_MIN: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the vendor, e.g. `0x10000` - * - * Sets the minimum allowed firmware size. - * - * Since: 1.1.2 - */ #define FU_QUIRKS_FIRMWARE_SIZE_MIN "FirmwareSizeMin" - -/** - * FU_QUIRKS_FIRMWARE_SIZE_MAX: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the vendor, e.g. `0x10000` - * - * Sets the maximum allowed firmware size. - * - * Since: 1.1.2 - */ #define FU_QUIRKS_FIRMWARE_SIZE_MAX "FirmwareSizeMax" - -/** - * FU_QUIRKS_INSTALL_DURATION: - * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` - * @value: the estimated time for flashing the device in seconds - * - * Sets the estimated time to flash the device - * - * Since: 1.1.3 - */ #define FU_QUIRKS_INSTALL_DURATION "InstallDuration" +#define FU_QUIRKS_VERSION_FORMAT "VersionFormat" G_END_DECLS diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 4f359614e..19423d5d2 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -6,7 +6,7 @@ #include "config.h" -#include +#include #include #include #include @@ -15,7 +15,10 @@ #include #include +#include "fu-archive.h" #include "fu-common-cab.h" +#include "fu-common-guid.h" +#include "fu-common-version.h" #include "fu-chunk.h" #include "fu-config.h" #include "fu-device-list.h" @@ -28,6 +31,7 @@ #include "fu-plugin-private.h" #include "fu-plugin-list.h" #include "fu-progressbar.h" +#include "fu-hash.h" #include "fu-hwids.h" #include "fu-smbios.h" #include "fu-test.h" @@ -39,28 +43,160 @@ #include "fu-keyring-pkcs7.h" #endif +static void +fu_self_test_mkroot (void) +{ + if (g_file_test ("/tmp/fwupd-self-test", G_FILE_TEST_EXISTS)) { + g_autoptr(GError) error = NULL; + if (!fu_common_rmtree ("/tmp/fwupd-self-test", &error)) + g_warning ("failed to mkroot: %s", error->message); + } + g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); +} + +static void +fu_archive_invalid_func (void) +{ + g_autofree gchar *filename = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(GBytes) data = NULL; + g_autoptr(GError) error = NULL; + + filename = fu_test_get_filename (TESTDATADIR, "metadata.xml"); + g_assert_nonnull (filename); + data = fu_common_get_contents_bytes (filename, &error); + g_assert_no_error (error); + g_assert_nonnull (data); + + archive = fu_archive_new (data, FU_ARCHIVE_FLAG_NONE, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); + g_assert_null (archive); +} + +static void +fu_engine_generate_md_func (void) +{ + const gchar *tmp; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); + g_autoptr(GBytes) data = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + + /* put cab file somewhere we can parse it */ + filename = fu_test_get_filename (TESTDATADIR, "colorhug/colorhug-als-3.0.2.cab"); + g_assert_nonnull (filename); + data = fu_common_get_contents_bytes (filename, &error); + g_assert_no_error (error); + g_assert_nonnull (data); + ret = fu_common_set_contents_bytes ("/tmp/fwupd-self-test/var/cache/fwupd/foo.cab", + data, &error); + g_assert_no_error (error); + g_assert (ret); + + /* load engine and check the device was found */ + ret = fu_engine_load (engine, &error); + g_assert_no_error (error); + g_assert (ret); + fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version (device, "1.2.3"); + component = fu_engine_get_component_by_guids (engine, device); + g_assert_nonnull (component); + + /* check remote ID set */ + tmp = xb_node_query_text (component, "../custom/value[@key='fwupd::RemoteId']", NULL); + g_assert_cmpstr (tmp, ==, "directory"); + + /* verify checksums */ + tmp = xb_node_query_text (component, "releases/release/checksum[@target='container']", NULL); + g_assert_cmpstr (tmp, !=, NULL); + tmp = xb_node_query_text (component, "releases/release/checksum[@target='content']", NULL); + g_assert_cmpstr (tmp, ==, NULL); +} + +static void +fu_archive_cab_func (void) +{ + g_autofree gchar *checksum1 = NULL; + g_autofree gchar *checksum2 = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(GBytes) data = NULL; + g_autoptr(GError) error = NULL; + GBytes *data_tmp; + + filename = fu_test_get_filename (TESTDATADIR, "colorhug/colorhug-als-3.0.2.cab"); + g_assert_nonnull (filename); + data = fu_common_get_contents_bytes (filename, &error); + g_assert_no_error (error); + g_assert_nonnull (data); + + archive = fu_archive_new (data, FU_ARCHIVE_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert_nonnull (archive); + + data_tmp = fu_archive_lookup_by_fn (archive, "firmware.metainfo.xml", &error); + g_assert_no_error (error); + g_assert_nonnull (data_tmp); + checksum1 = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, data_tmp); + g_assert_cmpstr (checksum1, ==, "8611114f51f7151f190de86a5c9259d79ff34216"); + + data_tmp = fu_archive_lookup_by_fn (archive, "firmware.bin", &error); + g_assert_no_error (error); + g_assert_nonnull (data_tmp); + checksum2 = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, data_tmp); + g_assert_cmpstr (checksum2, ==, "7c0ae84b191822bcadbdcbe2f74a011695d783c7"); + + data_tmp = fu_archive_lookup_by_fn (archive, "NOTGOINGTOEXIST.xml", &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert_null (data_tmp); +} + +static void +fu_common_version_guess_format_func (void) +{ + g_assert_cmpint (fu_common_version_guess_format (NULL), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format (""), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1234ac"), ==, FU_VERSION_FORMAT_PLAIN); + g_assert_cmpint (fu_common_version_guess_format ("1.2"), ==, FU_VERSION_FORMAT_PAIR); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3"), ==, FU_VERSION_FORMAT_TRIPLET); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3.4"), ==, FU_VERSION_FORMAT_QUAD); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3.4.5"), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1a.2b.3"), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1"), ==, FU_VERSION_FORMAT_PLAIN); +} + static void fu_engine_requirements_missing_func (void) { gboolean ret; - g_autoptr(AsApp) app = as_app_new (); - g_autoptr(AsRequire) req = as_require_new (); + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuInstallTask) task = NULL; g_autoptr(GError) error = NULL; + const gchar *xml = + "" + " " + " not.going.to.exist" + " " + ""; /* set up a dummy version */ fu_engine_add_runtime_version (engine, "org.test.dummy", "1.2.3"); /* make the component require one thing */ - as_require_set_kind (req, AS_REQUIRE_KIND_ID); - as_require_set_compare (req, AS_REQUIRE_COMPARE_GE); - as_require_set_version (req, "1.2.3"); - as_require_set_value (req, "not.going.to.exist"); - as_app_add_require (app, req); + 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 */ - task = fu_install_task_new (NULL, app); + task = fu_install_task_new (NULL, component); ret = fu_engine_check_requirements (engine, task, FWUPD_INSTALL_FLAG_NONE, &error); @@ -72,23 +208,31 @@ static void fu_engine_requirements_unsupported_func (void) { gboolean ret; - g_autoptr(AsApp) app = as_app_new (); - g_autoptr(AsRequire) req = as_require_new (); + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuInstallTask) task = NULL; g_autoptr(GError) error = NULL; + const gchar *xml = + "" + " " + " " + " " + ""; /* set up a dummy version */ fu_engine_add_runtime_version (engine, "org.test.dummy", "1.2.3"); /* make the component require one thing that we don't support */ - as_require_set_kind (req, AS_REQUIRE_KIND_LAST); - as_require_set_compare (req, AS_REQUIRE_COMPARE_GE); - as_require_set_version (req, "2.6.0"); - as_app_add_require (app, req); + 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 */ - task = fu_install_task_new (NULL, app); + task = fu_install_task_new (NULL, component); ret = fu_engine_check_requirements (engine, task, FWUPD_INSTALL_FLAG_NONE, &error); @@ -100,25 +244,32 @@ static void fu_engine_requirements_func (void) { gboolean ret; - g_autoptr(AsApp) app = as_app_new (); - g_autoptr(AsRequire) req = as_require_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuInstallTask) task = NULL; g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " org.test.dummy" + " " + ""; /* set up some dummy versions */ fu_engine_add_runtime_version (engine, "org.test.dummy", "1.2.3"); fu_engine_add_runtime_version (engine, "com.hughski.colorhug", "7.8.9"); /* make the component require one thing */ - as_require_set_kind (req, AS_REQUIRE_KIND_ID); - as_require_set_compare (req, AS_REQUIRE_COMPARE_GE); - as_require_set_version (req, "1.2.3"); - as_require_set_value (req, "org.test.dummy"); - as_app_add_require (app, req); + 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 */ - task = fu_install_task_new (NULL, app); + task = fu_install_task_new (NULL, component); ret = fu_engine_check_requirements (engine, task, FWUPD_INSTALL_FLAG_NONE, &error); @@ -130,17 +281,28 @@ static void fu_engine_requirements_device_func (void) { gboolean ret; - g_autoptr(AsApp) app = as_app_new (); - g_autoptr(AsChecksum) csum = as_checksum_new (); - g_autoptr(AsRequire) req1 = as_require_new (); - g_autoptr(AsRequire) req2 = as_require_new (); - g_autoptr(AsRequire) req3 = as_require_new (); - g_autoptr(AsProvide) prov = as_provide_new (); - g_autoptr(AsRelease) rel = as_release_new (); g_autoptr(FuDevice) device = fu_device_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuInstallTask) task = NULL; g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " " + " bootloader" + " vendor-id" + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; /* set up a dummy device */ fu_device_set_version (device, "1.2.3"); @@ -150,35 +312,15 @@ fu_engine_requirements_device_func (void) fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); /* make the component require three things */ - as_require_set_kind (req1, AS_REQUIRE_KIND_FIRMWARE); - as_require_set_compare (req1, AS_REQUIRE_COMPARE_GE); - as_require_set_version (req1, "1.2.3"); - as_app_add_require (app, req1); - as_require_set_kind (req2, AS_REQUIRE_KIND_FIRMWARE); - as_require_set_compare (req2, AS_REQUIRE_COMPARE_EQ); - as_require_set_version (req2, "4.5.6"); - as_require_set_value (req2, "bootloader"); - as_app_add_require (app, req3); - as_require_set_kind (req3, AS_REQUIRE_KIND_FIRMWARE); - as_require_set_compare (req3, AS_REQUIRE_COMPARE_EQ); - as_require_set_version (req3, "FFFF"); - as_require_set_value (req3, "vendor-id"); - as_app_add_require (app, req3); - - /* add release */ - as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT); - as_checksum_set_filename (csum, "bios.bin"); - as_release_set_version (rel, "1.2.4"); - as_release_add_checksum (rel, csum); - as_app_add_release (app, rel); - - /* add GUID to match */ - as_provide_set_kind (prov, AS_PROVIDE_KIND_FIRMWARE_FLASHED); - as_provide_set_value (prov, "12345678-1234-1234-1234-123456789012"); - as_app_add_provide (app, prov); + 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 */ - task = fu_install_task_new (device, app); + task = fu_install_task_new (device, component); ret = fu_engine_check_requirements (engine, task, FWUPD_INSTALL_FLAG_NONE, &error); @@ -190,16 +332,31 @@ static void fu_engine_requirements_other_device_func (void) { gboolean ret; - g_autoptr(AsApp) app = as_app_new (); - g_autoptr(AsChecksum) csum = as_checksum_new (); - g_autoptr(AsProvide) prov = as_provide_new (); - g_autoptr(AsRelease) rel = as_release_new (); - g_autoptr(AsRequire) req1 = as_require_new (); g_autoptr(FuDevice) device1 = fu_device_new (); g_autoptr(FuDevice) device2 = fu_device_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuInstallTask) task = NULL; g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + const gchar *xml = + "" + " " + " 1ff60ab2-3905-06a1-b476-0371f00c9e9b" + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* set up a dummy device */ fu_device_set_version (device1, "1.2.3"); @@ -211,30 +368,19 @@ fu_engine_requirements_other_device_func (void) fu_device_set_name (device2, "Secondary firmware"); fu_device_set_version (device2, "4.5.6"); fu_device_set_vendor_id (device2, "FFFF"); - fu_device_add_guid (device2, "00000000-0000-0000-0000-000000000000"); + fu_device_add_guid (device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b"); fu_engine_add_device (engine, device2); - /* make the component require another device version */ - as_require_set_kind (req1, AS_REQUIRE_KIND_FIRMWARE); - as_require_set_compare (req1, AS_REQUIRE_COMPARE_GT); - as_require_set_version (req1, "4.0.0"); - as_require_set_value (req1, "00000000-0000-0000-0000-000000000000"); - as_app_add_require (app, req1); - - /* add release */ - as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT); - as_checksum_set_filename (csum, "bios.bin"); - as_release_set_version (rel, "1.2.4"); - as_release_add_checksum (rel, csum); - as_app_add_release (app, rel); - - /* add GUID to match */ - as_provide_set_kind (prov, AS_PROVIDE_KIND_FIRMWARE_FLASHED); - as_provide_set_value (prov, "12345678-1234-1234-1234-123456789012"); - as_app_add_provide (app, prov); + /* import firmware metainfo */ + 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 */ - task = fu_install_task_new (device1, app); + task = fu_install_task_new (device1, component); ret = fu_engine_check_requirements (engine, task, FWUPD_INSTALL_FLAG_NONE, &error); @@ -251,6 +397,10 @@ fu_engine_device_priority_func (void) g_autoptr(FuDevice) device = NULL; g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(GError) error = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* add low prio then high then low */ fu_device_set_id (device1, "id1"); @@ -294,6 +444,10 @@ fu_engine_device_parent_func (void) g_autoptr(FuDevice) device2 = fu_device_new (); g_autoptr(FuDevice) device3 = fu_device_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* add child */ fu_device_set_id (device1, "child"); @@ -338,9 +492,14 @@ fu_engine_partial_hash_func (void) g_autoptr(GError) error = NULL; g_autoptr(GError) error_none = NULL; g_autoptr(GError) error_both = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* set up dummy plugin */ fu_plugin_set_name (plugin, "test"); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_engine_add_plugin (engine, plugin); /* add two dummy devices */ @@ -394,6 +553,10 @@ fu_engine_device_unlock_func (void) g_autoptr(FuDevice) device = fu_device_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(GError) error = NULL; + g_autoptr(GFile) file = 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, &error); @@ -403,9 +566,17 @@ fu_engine_device_unlock_func (void) /* add the hardcoded 'fwupd' metadata */ filename = fu_test_get_filename (TESTDATADIR, "metadata.xml"); g_assert (filename != NULL); - ret = fu_engine_load_metadata_from_file (engine, filename, NULL, &error); + file = g_file_new_for_path (filename); + ret = xb_builder_source_load_file (source, file, + XB_BUILDER_SOURCE_FLAG_NONE, + NULL, &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, "UEFI-dummy-dev0"); @@ -420,15 +591,16 @@ fu_engine_device_unlock_func (void) static void fu_engine_require_hwid_func (void) { - AsApp *app; gboolean ret; g_autofree gchar *filename = NULL; - g_autoptr(AsStore) store = NULL; g_autoptr(FuDevice) device = fu_device_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuInstallTask) task = NULL; g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + g_autoptr(XbSilo) silo = NULL; #if !defined(HAVE_GCAB_0_8) && defined(__s390x__) /* See https://github.com/hughsie/fwupd/issues/318 for more information */ @@ -436,6 +608,9 @@ fu_engine_require_hwid_func (void) return; #endif + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); + /* load engine to get FuConfig set up */ ret = fu_engine_load (engine, &error); g_assert_no_error (error); @@ -447,9 +622,9 @@ fu_engine_require_hwid_func (void) blob_cab = fu_common_get_contents_bytes (filename, &error); g_assert_no_error (error); g_assert (blob_cab != NULL); - store = fu_engine_get_store_from_blob (engine, blob_cab, &error); + silo = fu_engine_get_silo_from_blob (engine, blob_cab, &error); g_assert_no_error (error); - g_assert (store != NULL); + g_assert_nonnull (silo); /* add a dummy device */ fu_device_set_id (device, "test_device"); @@ -458,12 +633,13 @@ fu_engine_require_hwid_func (void) fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); fu_engine_add_device (engine, device); - /* get app */ - app = as_store_get_app_by_id (store, "com.hughski.test.firmware"); - g_assert_nonnull (app); + /* get component */ + component = xb_silo_query_first (silo, "components/component/id[text()='com.hughski.test.firmware']/..", &error); + g_assert_no_error (error); + g_assert_nonnull (component); /* check requirements */ - task = fu_install_task_new (device, app); + task = fu_install_task_new (device, component); ret = fu_engine_check_requirements (engine, task, FWUPD_INSTALL_FLAG_NONE, &error); @@ -489,6 +665,13 @@ fu_engine_downgrade_func (void) g_autoptr(GPtrArray) releases = NULL; g_autoptr(GPtrArray) releases_up = NULL; g_autoptr(GPtrArray) remotes = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + + /* ensure empty tree */ + fu_self_test_mkroot (); + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* write a broken file */ ret = g_file_set_contents ("/tmp/fwupd-self-test/broken.xml.gz", @@ -556,10 +739,6 @@ fu_engine_downgrade_func (void) g_assert_no_error (error); g_assert (ret); - /* expect just one broken remote to fail */ - g_test_expect_message ("FuEngine", G_LOG_LEVEL_WARNING, - "failed to load remote broken: *"); - testdatadir = fu_test_get_filename (TESTDATADIR, "."); g_assert (testdatadir != NULL); g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); @@ -573,7 +752,7 @@ fu_engine_downgrade_func (void) remotes = fu_engine_get_remotes (engine, &error); g_assert_no_error (error); g_assert (remotes != NULL); - g_assert_cmpint (remotes->len, ==, 3); + g_assert_cmpint (remotes->len, ==, 4); /* ensure there are no devices already */ devices_pre = fu_engine_get_devices (engine, &error); @@ -622,28 +801,103 @@ fu_engine_downgrade_func (void) g_assert_cmpstr (fwupd_release_get_version (rel), ==, "1.2.2"); } +static void +fu_engine_install_duration_func (void) +{ + FwupdRelease *rel; + gboolean ret; + g_autofree gchar *testdatadir = NULL; + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) releases = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + + /* ensure empty tree */ + fu_self_test_mkroot (); + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); + + /* write the main file */ + ret = g_file_set_contents ("/tmp/fwupd-self-test/stable.xml", + "" + " " + " test" + " " + " aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + " " + " " + " " + " https://test.org/foo.cab" + " deadbeefdeadbeefdeadbeefdeadbeef" + " deadbeefdeadbeefdeadbeefdeadbeef" + " " + " " + " " + "", -1, &error); + g_assert_no_error (error); + g_assert (ret); + + testdatadir = fu_test_get_filename (TESTDATADIR, "."); + g_assert (testdatadir != NULL); + g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); + ret = fu_engine_load (engine, &error); + g_assert_no_error (error); + g_assert (ret); + + /* add a device so we can get the install duration */ + fu_device_set_version (device, "1.2.3"); + fu_device_set_id (device, "test_device"); + fu_device_add_guid (device, "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); + fu_device_set_install_duration (device, 999); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_engine_add_device (engine, device); + devices = fu_engine_get_devices (engine, &error); + g_assert_no_error (error); + g_assert (devices != NULL); + g_assert_cmpint (devices->len, ==, 1); + g_assert (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)); + + /* check the release install duration */ + releases = fu_engine_get_releases (engine, fu_device_get_id (device), &error); + g_assert_no_error (error); + g_assert (releases != NULL); + g_assert_cmpint (releases->len, ==, 1); + rel = FWUPD_RELEASE (g_ptr_array_index (releases, 0)); + g_assert_cmpint (fwupd_release_get_install_duration (rel), ==, 120); +} + static void fu_engine_history_func (void) { - AsApp *app; gboolean ret; + g_autofree gchar *checksum = NULL; g_autofree gchar *device_str_expected = NULL; g_autofree gchar *device_str = NULL; g_autofree gchar *filename = NULL; - g_autofree gchar *checksum = NULL; g_autofree gchar *testdatadir = NULL; - g_autoptr(AsStore) store = NULL; g_autoptr(FuDevice) device2 = NULL; - g_autoptr(FwupdDevice) device3 = NULL; - g_autoptr(FwupdDevice) device4 = NULL; g_autoptr(FuDevice) device = fu_device_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuHistory) history = NULL; g_autoptr(FuInstallTask) task = NULL; g_autoptr(FuPlugin) plugin = fu_plugin_new (); + g_autoptr(FwupdDevice) device3 = NULL; + g_autoptr(FwupdDevice) device4 = NULL; g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + g_autoptr(XbSilo) silo = NULL; + + /* ensure empty tree */ + fu_self_test_mkroot (); + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* set up dummy plugin */ ret = fu_plugin_open (plugin, PLUGINBUILDDIR "/libfu_plugin_test.so", &error); @@ -665,6 +919,7 @@ fu_engine_history_func (void) fu_device_set_name (device, "Test Device"); fu_device_set_plugin (device, "test"); fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); + fu_device_add_checksum (device, "0123456789abcdef0123456789abcdef01234567"); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_set_created (device, 1515338000); fu_engine_add_device (engine, device); @@ -679,16 +934,17 @@ fu_engine_history_func (void) blob_cab = fu_common_get_contents_bytes (filename, &error); g_assert_no_error (error); g_assert (blob_cab != NULL); - store = fu_engine_get_store_from_blob (engine, blob_cab, &error); + silo = fu_engine_get_silo_from_blob (engine, blob_cab, &error); g_assert_no_error (error); - g_assert (store != NULL); + g_assert_nonnull (silo); - /* get app */ - app = as_store_get_app_by_id (store, "com.hughski.test.firmware"); - g_assert_nonnull (app); + /* get component */ + component = xb_silo_query_first (silo, "components/component/id[text()='com.hughski.test.firmware']/..", &error); + g_assert_no_error (error); + g_assert_nonnull (component); /* install it */ - task = fu_install_task_new (device, app); + task = fu_install_task_new (device, component); ret = fu_engine_install (engine, task, blob_cab, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error (error); @@ -719,7 +975,8 @@ fu_engine_history_func (void) " [Release]\n" " Version: 1.2.3\n" " Checksum: SHA1(%s)\n" - " TrustFlags: none\n", + " TrustFlags: none\n" + " VersionFormat: triplet\n", checksum); ret = fu_test_compare_lines (device_str, device_str_expected, &error); g_assert_no_error (error); @@ -747,14 +1004,12 @@ fu_engine_history_func (void) static void fu_engine_history_error_func (void) { - AsApp *app; gboolean ret; + g_autofree gchar *checksum = NULL; g_autofree gchar *device_str_expected = NULL; g_autofree gchar *device_str = NULL; g_autofree gchar *filename = NULL; - g_autofree gchar *checksum = NULL; g_autofree gchar *testdatadir = NULL; - g_autoptr(AsStore) store = NULL; g_autoptr(FuDevice) device2 = NULL; g_autoptr(FuDevice) device = fu_device_new (); g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); @@ -765,6 +1020,12 @@ fu_engine_history_error_func (void) g_autoptr(GError) error2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + g_autoptr(XbSilo) silo = NULL; + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* set up dummy plugin */ g_setenv ("FWUPD_PLUGIN_TEST", "fail", TRUE); @@ -802,12 +1063,13 @@ fu_engine_history_error_func (void) blob_cab = fu_common_get_contents_bytes (filename, &error); g_assert_no_error (error); g_assert (blob_cab != NULL); - store = fu_engine_get_store_from_blob (engine, blob_cab, &error); + silo = fu_engine_get_silo_from_blob (engine, blob_cab, &error); g_assert_no_error (error); - g_assert (store != NULL); - app = as_store_get_app_by_id (store, "com.hughski.test.firmware"); - g_assert_nonnull (app); - task = fu_install_task_new (device, app); + g_assert_nonnull (silo); + component = xb_silo_query_first (silo, "components/component/id[text()='com.hughski.test.firmware']/..", &error); + g_assert_no_error (error); + g_assert_nonnull (component); + task = fu_install_task_new (device, component); ret = fu_engine_install (engine, task, blob_cab, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); @@ -843,7 +1105,8 @@ fu_engine_history_error_func (void) " [Release]\n" " Version: 1.2.3\n" " Checksum: SHA1(%s)\n" - " TrustFlags: none\n", + " TrustFlags: none\n" + " VersionFormat: triplet\n", checksum); ret = fu_test_compare_lines (device_str, device_str_expected, &error); g_assert_no_error (error); @@ -1241,6 +1504,15 @@ fu_device_list_func (void) "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); } +static void +fu_device_version_format_func (void) +{ + g_autoptr(FuDevice) device = fu_device_new (); + fu_device_set_version_format (device, FU_VERSION_FORMAT_TRIPLET); + fu_device_set_version (device, "Ver1.2.3 RELEASE"); + g_assert_cmpstr (fu_device_get_version (device), ==, "1.2.3"); +} + static void fu_device_open_refcount_func (void) { @@ -1573,6 +1845,34 @@ fu_plugin_quirks_device_func (void) g_assert (fu_device_has_flag (device_tmp, FWUPD_DEVICE_FLAG_UPDATABLE)); } +static void +fu_plugin_hash_func (void) +{ + GError *error = NULL; + g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); + g_autoptr(FuPlugin) plugin = fu_plugin_new (); + gboolean ret = FALSE; + + ret = fu_engine_load (engine, &error); + g_assert_no_error (error); + g_assert (ret); + + /* make sure not tainted */ + ret = fu_engine_get_tainted (engine); + g_assert_false (ret); + + /* create a tainted plugin */ + g_setenv ("FWUPD_PLUGIN_TEST", "build-hash", TRUE); + ret = fu_plugin_open (plugin, PLUGINBUILDDIR "/libfu_plugin_test.so", &error); + g_assert_no_error (error); + + /* make sure it tainted now */ + g_test_expect_message ("FuEngine", G_LOG_LEVEL_WARNING, "* has incorrect built version*"); + fu_engine_add_plugin (engine, plugin); + ret = fu_engine_get_tainted (engine); + g_assert_true (ret); +} + static void fu_plugin_module_func (void) { @@ -2279,13 +2579,17 @@ fu_plugin_composite_func (void) { GError *error = NULL; gboolean ret; - GPtrArray *apps; g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(FuPlugin) plugin = fu_plugin_new (); - g_autoptr(GPtrArray) devices = NULL; g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - g_autoptr(AsStore) store = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new (); + g_autoptr(XbSilo) silo = NULL; + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); /* create CAB file */ blob = _build_cab (GCAB_COMPRESSION_NONE, @@ -2308,6 +2612,9 @@ fu_plugin_composite_func (void) " \n" " \n" " \n" + " \n" + " plain\n" + " \n" "", "acme.module2.metainfo.xml", "\n" @@ -2318,6 +2625,9 @@ fu_plugin_composite_func (void) " \n" " \n" " \n" + " \n" + " plain\n" + " \n" "", "firmware.bin", "world", NULL); @@ -2325,11 +2635,13 @@ fu_plugin_composite_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_no_error (error); - g_assert_nonnull (store); - apps = as_store_get_apps (store); - g_assert_cmpint (apps->len, ==, 3); + g_assert_nonnull (silo); + components = xb_silo_query (silo, "components/component", 0, &error); + g_assert_no_error (error); + g_assert_nonnull (components); + g_assert_cmpint (components->len, ==, 3); /* set up dummy plugin */ g_setenv ("FWUPD_PLUGIN_TEST", "composite", TRUE); @@ -2370,8 +2682,8 @@ fu_plugin_composite_func (void) } /* produce install tasks */ - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); /* do any devices pass the requirements */ for (guint j = 0; j < devices->len; j++) { @@ -2380,14 +2692,14 @@ fu_plugin_composite_func (void) g_autoptr(GError) error_local = NULL; /* is this component valid for the device */ - task = fu_install_task_new (device, app); + task = fu_install_task_new (device, component); if (!fu_engine_check_requirements (engine, task, 0, &error_local)) { g_debug ("requirement on %s:%s failed: %s", fu_device_get_id (device), - as_app_get_id (app), + xb_node_query_text (component, "id", NULL), error_local->message); continue; } @@ -2432,16 +2744,16 @@ fu_plugin_composite_func (void) static void fu_common_store_cab_func (void) { - AsApp *app; - AsChecksum *csum; - AsRelease *rel; - AsRequire *req; GBytes *blob_tmp; - g_autoptr(AsStore) store = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbNode) csum = NULL; + g_autoptr(XbNode) rel = NULL; + g_autoptr(XbNode) req = NULL; + g_autoptr(XbSilo) silo = NULL; - /* create store */ + /* create silo */ blob = _build_cab (GCAB_COMPRESSION_NONE, "acme.metainfo.xml", "\n" @@ -2452,9 +2764,8 @@ fu_common_store_cab_func (void) " \n" " \n" " \n" - " \n" " 5\n" - " 7c211433f02071597741e6ff5a8ea34789abbf43\n" + " 7c211433f02071597741e6ff5a8ea34789abbf43\n" "

We fixed things

\n" "
\n" "
\n" @@ -2469,38 +2780,42 @@ fu_common_store_cab_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_no_error (error); - g_assert (store != NULL); + g_assert_nonnull (silo); /* verify */ - app = as_store_get_app_by_id (store, "com.acme.example.firmware"); - g_assert_nonnull (app); - rel = as_app_get_release_default (app); + component = xb_silo_query_first (silo, "components/component/id[text()='com.acme.example.firmware']/..", &error); + g_assert_no_error (error); + g_assert_nonnull (component); + rel = xb_node_query_first (component, "releases/release", &error); + g_assert_no_error (error); g_assert_nonnull (rel); - g_assert_cmpstr (as_release_get_version (rel), ==, "1.2.3"); - csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); - g_assert_cmpstr (as_checksum_get_value (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43"); - blob_tmp = as_release_get_blob (rel, "firmware.dfu"); + g_assert_cmpstr (xb_node_get_attr (rel, "version"), ==, "1.2.3"); + csum = xb_node_query_first (rel, "checksum[@target='content']", &error); + g_assert_nonnull (csum); + g_assert_cmpstr (xb_node_get_text (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43"); + blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.dfu)"); g_assert_nonnull (blob_tmp); - blob_tmp = as_release_get_blob (rel, "firmware.dfu.asc"); + blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.dfu.asc)"); g_assert_nonnull (blob_tmp); - req = as_app_get_require_by_value (app, AS_REQUIRE_KIND_ID, "org.freedesktop.fwupd"); + req = xb_node_query_first (component, "requires/id", &error); + g_assert_no_error (error); g_assert_nonnull (req); } static void fu_common_store_cab_unsigned_func (void) { - AsApp *app; - AsChecksum *csum; - AsRelease *rel; GBytes *blob_tmp; - g_autoptr(AsStore) store = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbNode) csum = NULL; + g_autoptr(XbNode) rel = NULL; + g_autoptr(XbSilo) silo = NULL; - /* create store */ + /* create silo */ blob = _build_cab (GCAB_COMPRESSION_NONE, "acme.metainfo.xml", "\n" @@ -2515,36 +2830,37 @@ fu_common_store_cab_unsigned_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_no_error (error); - g_assert (store != NULL); + g_assert_nonnull (silo); /* verify */ - app = as_store_get_app_by_id (store, "com.acme.example.firmware"); - g_assert_nonnull (app); - rel = as_app_get_release_default (app); + component = xb_silo_query_first (silo, "components/component/id[text()='com.acme.example.firmware']/..", &error); + g_assert_no_error (error); + g_assert_nonnull (component); + rel = xb_node_query_first (component, "releases/release", &error); + g_assert_no_error (error); g_assert_nonnull (rel); - g_assert_cmpstr (as_release_get_version (rel), ==, "1.2.3"); - csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); - g_assert_cmpstr (as_checksum_get_value (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43"); - blob_tmp = as_release_get_blob (rel, "firmware.bin"); + g_assert_cmpstr (xb_node_get_attr (rel, "version"), ==, "1.2.3"); + csum = xb_node_query_first (rel, "checksum[@target='content']", &error); + g_assert_null (csum); + blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.bin)"); g_assert_nonnull (blob_tmp); - blob_tmp = as_release_get_blob (rel, "firmware.bin.asc"); + blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.bin.asc)"); g_assert_null (blob_tmp); } static void fu_common_store_cab_folder_func (void) { - AsApp *app; - AsChecksum *csum; - AsRelease *rel; GBytes *blob_tmp; - g_autoptr(AsStore) store = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbNode) rel = NULL; + g_autoptr(XbSilo) silo = NULL; - /* create store */ + /* create silo */ blob = _build_cab (GCAB_COMPRESSION_NONE, "lvfs\\acme.metainfo.xml", "\n" @@ -2559,26 +2875,26 @@ fu_common_store_cab_folder_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_no_error (error); - g_assert (store != NULL); + g_assert_nonnull (silo); /* verify */ - app = as_store_get_app_by_id (store, "com.acme.example.firmware"); - g_assert_nonnull (app); - rel = as_app_get_release_default (app); + component = xb_silo_query_first (silo, "components/component/id[text()='com.acme.example.firmware']/..", &error); + g_assert_no_error (error); + g_assert_nonnull (component); + rel = xb_node_query_first (component, "releases/release", &error); + g_assert_no_error (error); g_assert_nonnull (rel); - g_assert_cmpstr (as_release_get_version (rel), ==, "1.2.3"); - csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); - g_assert_cmpstr (as_checksum_get_value (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43"); - blob_tmp = as_release_get_blob (rel, "firmware.bin"); + g_assert_cmpstr (xb_node_get_attr (rel, "version"), ==, "1.2.3"); + blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.bin)"); g_assert_nonnull (blob_tmp); } static void fu_common_store_cab_error_no_metadata_func (void) { - g_autoptr(AsStore) store = NULL; + g_autoptr(XbSilo) silo = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; @@ -2590,15 +2906,15 @@ fu_common_store_cab_error_no_metadata_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); - g_assert (store == NULL); + g_assert_null (silo); } static void fu_common_store_cab_error_wrong_size_func (void) { - g_autoptr(AsStore) store = NULL; + g_autoptr(XbSilo) silo = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; @@ -2619,15 +2935,15 @@ fu_common_store_cab_error_wrong_size_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); - g_assert (store == NULL); + g_assert_null (silo); } static void fu_common_store_cab_error_missing_file_func (void) { - g_autoptr(AsStore) store = NULL; + g_autoptr(XbSilo) silo = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; @@ -2647,15 +2963,15 @@ fu_common_store_cab_error_missing_file_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); - g_assert (store == NULL); + g_assert_null (silo); } static void fu_common_store_cab_error_size_func (void) { - g_autoptr(AsStore) store = NULL; + g_autoptr(XbSilo) silo = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; @@ -2673,15 +2989,15 @@ fu_common_store_cab_error_size_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 123, &error); + silo = fu_common_cab_build_silo (blob, 123, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); - g_assert (store == NULL); + g_assert_null (silo); } static void fu_common_store_cab_error_wrong_checksum_func (void) { - g_autoptr(AsStore) store = NULL; + g_autoptr(XbSilo) silo = NULL; g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; @@ -2701,9 +3017,9 @@ fu_common_store_cab_error_wrong_checksum_func (void) g_test_skip ("libgcab too old"); return; } - store = fu_common_store_from_cab_bytes (blob, 10240, &error); + silo = fu_common_cab_build_silo (blob, 10240, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); - g_assert (store == NULL); + g_assert_null (silo); } static gboolean @@ -2729,7 +3045,7 @@ fu_device_poll_func (void) fu_test_loop_run_with_timeout (100); fu_test_loop_quit (); cnt = fu_device_get_metadata_integer (device, "cnt"); - g_assert_cmpint (cnt, >=, 9); + g_assert_cmpint (cnt, >=, 8); /* disable the poll */ fu_device_set_poll_interval (device, 0); @@ -2838,6 +3154,162 @@ fu_common_strstrip_func (void) } } +static void +fu_common_guid_func (void) +{ + g_autofree gchar *guid1 = NULL; + g_autofree gchar *guid2 = NULL; + + /* invalid */ + g_assert (!fu_common_guid_is_valid (NULL)); + g_assert (!fu_common_guid_is_valid ("")); + g_assert (!fu_common_guid_is_valid ("1ff60ab2-3905-06a1-b476")); + g_assert (!fu_common_guid_is_valid ("1ff60ab2-XXXX-XXXX-XXXX-0371f00c9e9b")); + g_assert (!fu_common_guid_is_valid (" 1ff60ab2-3905-06a1-b476-0371f00c9e9b")); + g_assert (!fu_common_guid_is_valid ("00000000-0000-0000-0000-000000000000")); + + /* valid */ + g_assert (fu_common_guid_is_valid ("1ff60ab2-3905-06a1-b476-0371f00c9e9b")); + + /* make valid */ + guid1 = fu_common_guid_from_string ("python.org"); + g_assert_cmpstr (guid1, ==, "886313e1-3b8a-5372-9b90-0c9aee199e5d"); + guid2 = fu_common_guid_from_string ("8086:0406"); + g_assert_cmpstr (guid2, ==, "1fbd1f2c-80f4-5d7c-a6ad-35c7b9bd5486"); +} + +static void +fu_common_version_func (void) +{ + guint i; + struct { + guint32 val; + const gchar *ver; + FuVersionFormat flags; + } version_from_uint32[] = { + { 0x0, "0.0.0.0", FU_VERSION_FORMAT_QUAD }, + { 0xff, "0.0.0.255", FU_VERSION_FORMAT_QUAD }, + { 0xff01, "0.0.255.1", FU_VERSION_FORMAT_QUAD }, + { 0xff0001, "0.255.0.1", FU_VERSION_FORMAT_QUAD }, + { 0xff000100, "255.0.1.0", FU_VERSION_FORMAT_QUAD }, + { 0x0, "0.0.0", FU_VERSION_FORMAT_TRIPLET }, + { 0xff, "0.0.255", FU_VERSION_FORMAT_TRIPLET }, + { 0xff01, "0.0.65281", FU_VERSION_FORMAT_TRIPLET }, + { 0xff0001, "0.255.1", FU_VERSION_FORMAT_TRIPLET }, + { 0xff000100, "255.0.256", FU_VERSION_FORMAT_TRIPLET }, + { 0x0, "0", FU_VERSION_FORMAT_PLAIN }, + { 0xff000100, "4278190336", FU_VERSION_FORMAT_PLAIN }, + { 0x0, "11.0.0.0", FU_VERSION_FORMAT_INTEL_ME }, + { 0xffffffff, "18.31.255.65535", FU_VERSION_FORMAT_INTEL_ME }, + { 0x0b32057a, "11.11.50.1402", FU_VERSION_FORMAT_INTEL_ME }, + { 0xb8320d84, "11.8.50.3460", FU_VERSION_FORMAT_INTEL_ME2 }, + { 0, NULL } + }; + struct { + guint16 val; + const gchar *ver; + FuVersionFormat flags; + } version_from_uint16[] = { + { 0x0, "0.0", FU_VERSION_FORMAT_PAIR }, + { 0xff, "0.255", FU_VERSION_FORMAT_PAIR }, + { 0xff01, "255.1", FU_VERSION_FORMAT_PAIR }, + { 0x0, "0.0", FU_VERSION_FORMAT_BCD }, + { 0x0110, "1.10", FU_VERSION_FORMAT_BCD }, + { 0x9999, "99.99", FU_VERSION_FORMAT_BCD }, + { 0x0, "0", FU_VERSION_FORMAT_PLAIN }, + { 0x1234, "4660", FU_VERSION_FORMAT_PLAIN }, + { 0, NULL } + }; + struct { + const gchar *old; + const gchar *new; + } version_parse[] = { + { "0", "0" }, + { "0x1a", "0.0.26" }, + { "257", "0.0.257" }, + { "1.2.3", "1.2.3" }, + { "0xff0001", "0.255.1" }, + { "16711681", "0.255.1" }, + { "20150915", "20150915" }, + { "dave", "dave" }, + { "0x1x", "0x1x" }, + { NULL, NULL } + }; + + /* check version conversion */ + for (i = 0; version_from_uint32[i].ver != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_common_version_from_uint32 (version_from_uint32[i].val, + version_from_uint32[i].flags); + g_assert_cmpstr (ver, ==, version_from_uint32[i].ver); + } + for (i = 0; version_from_uint16[i].ver != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_common_version_from_uint16 (version_from_uint16[i].val, + version_from_uint16[i].flags); + g_assert_cmpstr (ver, ==, version_from_uint16[i].ver); + } + + /* check version parsing */ + for (i = 0; version_parse[i].old != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_common_version_parse (version_parse[i].old); + g_assert_cmpstr (ver, ==, version_parse[i].new); + } +} + +static void +fu_common_vercmp_func (void) +{ + /* same */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("001.002.003", "001.002.003"), ==, 0); + + /* same, not dotted decimal */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "0x1020003"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("0x10203", "0x10203"), ==, 0); + + /* upgrade and downgrade */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.4"), <, 0); + g_assert_cmpint (fu_common_vercmp ("001.002.000", "001.002.009"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.2"), >, 0); + g_assert_cmpint (fu_common_vercmp ("001.002.009", "001.002.000"), >, 0); + + /* unequal depth */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3.1"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3.1", "1.2.4"), <, 0); + + /* mixed-alpha-numeric */ + g_assert_cmpint (fu_common_vercmp ("1.2.3a", "1.2.3a"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3a", "1.2.3b"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3b", "1.2.3a"), >, 0); + + /* alpha version append */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3a"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3a", "1.2.3"), >, 0); + + /* alpha only */ + g_assert_cmpint (fu_common_vercmp ("alpha", "alpha"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("alpha", "beta"), <, 0); + g_assert_cmpint (fu_common_vercmp ("beta", "alpha"), >, 0); + + /* alpha-compare */ + g_assert_cmpint (fu_common_vercmp ("1.2a.3", "1.2a.3"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("1.2a.3", "1.2b.3"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2b.3", "1.2a.3"), >, 0); + + /* tilde is all-powerful */ + g_assert_cmpint (fu_common_vercmp ("1.2.3~rc1", "1.2.3~rc1"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3~rc1", "1.2.3"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3~rc1"), >, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3~rc2", "1.2.3~rc1"), >, 0); + + /* invalid */ + g_assert_cmpint (fu_common_vercmp ("1", NULL), ==, G_MAXINT); + g_assert_cmpint (fu_common_vercmp (NULL, "1"), ==, G_MAXINT); + g_assert_cmpint (fu_common_vercmp (NULL, NULL), ==, G_MAXINT); +} + int main (int argc, char **argv) { @@ -2852,12 +3324,14 @@ main (int argc, char **argv) g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR_SRC, TRUE); g_setenv ("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE); - fu_common_rmtree ("/tmp/fwupd-self-test", NULL); - g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); + /* ensure empty tree */ + fu_self_test_mkroot (); /* tests go here */ if (g_test_slow ()) g_test_add_func ("/fwupd/progressbar", fu_progressbar_func); + g_test_add_func ("/fwupd/archive{invalid}", fu_archive_invalid_func); + g_test_add_func ("/fwupd/archive{cab}", fu_archive_cab_func); g_test_add_func ("/fwupd/engine{requirements-other-device}", fu_engine_requirements_other_device_func); g_test_add_func ("/fwupd/device{incorporate}", fu_device_incorporate_func); g_test_add_func ("/fwupd/device{poll}", fu_device_poll_func); @@ -2865,6 +3339,7 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/device-locker{fail}", fu_device_locker_fail_func); g_test_add_func ("/fwupd/device{metadata}", fu_device_metadata_func); g_test_add_func ("/fwupd/device{open-refcount}", fu_device_open_refcount_func); + g_test_add_func ("/fwupd/device{version-format}", fu_device_version_format_func); g_test_add_func ("/fwupd/device-list", fu_device_list_func); g_test_add_func ("/fwupd/device-list{delay}", fu_device_list_delay_func); g_test_add_func ("/fwupd/device-list{compatible}", fu_device_list_compatible_func); @@ -2883,6 +3358,8 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/engine{requirements-device}", fu_engine_requirements_device_func); g_test_add_func ("/fwupd/engine{device-auto-parent}", fu_engine_device_parent_func); g_test_add_func ("/fwupd/engine{device-priority}", fu_engine_device_priority_func); + g_test_add_func ("/fwupd/engine{install-duration}", fu_engine_install_duration_func); + g_test_add_func ("/fwupd/engine{generate-md}", fu_engine_generate_md_func); g_test_add_func ("/fwupd/hwids", fu_hwids_func); g_test_add_func ("/fwupd/smbios", fu_smbios_func); g_test_add_func ("/fwupd/smbios3", fu_smbios3_func); @@ -2898,7 +3375,12 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/plugin{composite}", fu_plugin_composite_func); g_test_add_func ("/fwupd/keyring{gpg}", fu_keyring_gpg_func); g_test_add_func ("/fwupd/keyring{pkcs7}", fu_keyring_pkcs7_func); + g_test_add_func ("/fwupd/plugin{build-hash}", fu_plugin_hash_func); g_test_add_func ("/fwupd/chunk", fu_chunk_func); + g_test_add_func ("/fwupd/common{version-guess-format}", fu_common_version_guess_format_func); + g_test_add_func ("/fwupd/common{guid}", fu_common_guid_func); + g_test_add_func ("/fwupd/common{version}", fu_common_version_func); + g_test_add_func ("/fwupd/common{vercmp}", fu_common_vercmp_func); g_test_add_func ("/fwupd/common{strstrip}", fu_common_strstrip_func); g_test_add_func ("/fwupd/common{endian}", fu_common_endian_func); g_test_add_func ("/fwupd/common{cab-success}", fu_common_store_cab_func); diff --git a/src/fu-tool.c b/src/fu-tool.c index b2588494c..f48b0ca5b 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -9,6 +9,7 @@ #include "config.h" #include +#include #include #include #include @@ -16,24 +17,30 @@ #include #include +#include "fu-device-private.h" #include "fu-engine.h" #include "fu-plugin-private.h" #include "fu-progressbar.h" #include "fu-smbios.h" #include "fu-util-common.h" #include "fu-debug.h" +#include "fwupd-common-private.h" #define SYSTEMD_SERVICE "org.freedesktop.systemd1" #define SYSTEMD_OBJECT_PATH "/org/freedesktop/systemd1" #define SYSTEMD_MANAGER_INTERFACE "org.freedesktop.systemd1.Manager" #define SYSTEMD_FWUPD_UNIT "fwupd.service" -/* this is only valid in this file */ -#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1) - /* custom return code */ #define EXIT_NOTHING_TO_DO 2 +typedef enum { + FU_UTIL_OPERATION_UNKNOWN, + FU_UTIL_OPERATION_UPDATE, + FU_UTIL_OPERATION_INSTALL, + FU_UTIL_OPERATION_LAST +} FuUtilOperation; + typedef struct { GCancellable *cancellable; GMainLoop *loop; @@ -41,10 +48,14 @@ typedef struct { GPtrArray *cmd_array; FuEngine *engine; FuProgressbar *progressbar; + gboolean no_reboot_check; FwupdInstallFlags flags; gboolean show_all_devices; /* only valid in update and downgrade */ + FuUtilOperation current_operation; FwupdDevice *current_device; + gchar *current_message; + FwupdDeviceFlags completion_flags; } FuUtilPrivate; typedef gboolean (*FuUtilPrivateCb) (FuUtilPrivate *util, @@ -120,7 +131,13 @@ fu_util_start_engine (FuUtilPrivate *priv, GError **error) } } - return fu_engine_load (priv->engine, error); + if (!fu_engine_load (priv->engine, error)) + return FALSE; + if (fu_engine_get_tainted (priv->engine)) { + g_printerr ("WARNING: This tool has loaded 3rd party code and " + "is no longer supported by the upstream developers!\n"); + } + return TRUE; } static gint @@ -285,6 +302,7 @@ fu_util_private_free (FuUtilPrivate *priv) g_object_unref (priv->progressbar); if (priv->context != NULL) g_option_context_free (priv->context); + g_free (priv->current_message); g_free (priv); } @@ -372,6 +390,49 @@ fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, error)) + return FALSE; + + /* get devices from daemon */ + devices = fu_engine_get_devices (priv->engine, error); + if (devices == NULL) + return FALSE; + 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; + + /* not going to have results, so save a D-Bus round-trip */ + 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 (priv->engine, + fwupd_device_get_id (dev), + &error_local); + if (rels == NULL) { + g_printerr ("%s\n", error_local->message); + continue; + } + g_print ("%s", fwupd_device_to_string (dev)); + g_print (" Release information:\n"); + /* print all releases */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel = g_ptr_array_index (rels, j); + g_print ("%s\n", fwupd_release_to_string (rel)); + } + } + + /* success */ + return TRUE; +} + static gboolean fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -531,7 +592,7 @@ fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error) } static void -fu_util_install_device_changed_cb (FwupdClient *client, +fu_util_update_device_changed_cb (FwupdClient *client, FwupdDevice *device, FuUtilPrivate *priv) { @@ -543,11 +604,40 @@ fu_util_install_device_changed_cb (FwupdClient *client, return; /* show message in progressbar */ - /* TRANSLATORS: %1 is a device name */ - str = g_strdup_printf (_("Installing %s"), - fwupd_device_get_name (device)); - fu_progressbar_set_title (priv->progressbar, str); + if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) { + /* TRANSLATORS: %1 is a device name */ + str = g_strdup_printf (_("Updating %s…"), + fwupd_device_get_name (device)); + fu_progressbar_set_title (priv->progressbar, str); + } else if (priv->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_progressbar_set_title (priv->progressbar, str); + } else { + g_warning ("no FuUtilOperation set"); + } g_set_object (&priv->current_device, device); + + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + else if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + if (priv->current_message == NULL) { + const gchar *tmp = fwupd_device_get_update_message (priv->current_device); + if (tmp != NULL) + priv->current_message = g_strdup (tmp); + } +} + +static void +fu_util_display_current_message (FuUtilPrivate *priv) +{ + if (priv->current_message == NULL) + return; + g_print ("%s\n", priv->current_message); + g_clear_pointer (&priv->current_message, g_free); } static gboolean @@ -587,16 +677,23 @@ fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error) return FALSE; } + priv->current_operation = FU_UTIL_OPERATION_INSTALL; g_signal_connect (priv->engine, "device-changed", - G_CALLBACK (fu_util_install_device_changed_cb), priv); + G_CALLBACK (fu_util_update_device_changed_cb), priv); /* write bare firmware */ - return fu_engine_install_blob (priv->engine, device, - NULL, /* blob_cab */ - blob_fw, - NULL, /* version */ - priv->flags, - error); + if (!fu_engine_install_blob (priv->engine, device, + NULL, /* blob_cab */ + blob_fw, + NULL, /* version */ + priv->flags, + error)) + return FALSE; + + fu_util_display_current_message (priv); + + /* success */ + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } static gint @@ -651,13 +748,13 @@ fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GErro static gboolean fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) { - GPtrArray *apps; g_autofree gchar *filename = NULL; - g_autoptr(AsStore) store = NULL; g_autoptr(GBytes) blob_cab = NULL; + g_autoptr(GPtrArray) components = NULL; g_autoptr(GPtrArray) devices_possible = NULL; g_autoptr(GPtrArray) errors = NULL; g_autoptr(GPtrArray) install_tasks = NULL; + g_autoptr(XbSilo) silo = NULL; /* load engine */ if (!fu_util_start_engine (priv, error)) @@ -689,22 +786,24 @@ fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) if (filename == NULL) return FALSE; - /* parse store */ + /* parse silo */ blob_cab = fu_common_get_contents_bytes (filename, error); if (blob_cab == NULL) { fu_util_maybe_prefix_sandbox_error (filename, error); return FALSE; } - store = fu_engine_get_store_from_blob (priv->engine, blob_cab, error); - if (store == NULL) + silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error); + if (silo == NULL) + return FALSE; + components = xb_silo_query (silo, "components/component", 0, error); + if (components == NULL) return FALSE; - apps = as_store_get_apps (store); - /* for each component in the store */ + /* for each component in the silo */ errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free); install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); /* do any devices pass the requirements */ for (guint j = 0; j < devices_possible->len; j++) { @@ -713,18 +812,21 @@ fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) g_autoptr(GError) error_local = NULL; /* is this component valid for the device */ - task = fu_install_task_new (device, app); + task = fu_install_task_new (device, component); if (!fu_engine_check_requirements (priv->engine, task, priv->flags, &error_local)) { g_debug ("requirement on %s:%s failed: %s", fu_device_get_id (device), - as_app_get_id (app), + xb_node_query_text (component, "id", NULL), error_local->message); g_ptr_array_add (errors, g_steal_pointer (&error_local)); continue; } + /* if component should have an update message from CAB */ + fu_device_incorporate_from_component (device, component); + /* success */ g_ptr_array_add (install_tasks, g_steal_pointer (&task)); } @@ -740,15 +842,104 @@ fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) return FALSE; } + priv->current_operation = FU_UTIL_OPERATION_INSTALL; g_signal_connect (priv->engine, "device-changed", - G_CALLBACK (fu_util_install_device_changed_cb), priv); + G_CALLBACK (fu_util_update_device_changed_cb), priv); /* install all the tasks */ if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error)) return FALSE; + fu_util_display_current_message (priv); + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug ("skipping reboot check"); + return TRUE; + } + /* success */ - return TRUE; + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, error)) + return FALSE; + + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + g_signal_connect (priv->engine, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + + devices = fu_engine_get_devices (priv->engine, error); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devices, i); + FwupdRelease *rel; + const gchar *remote_id; + const gchar *device_id; + const gchar *uri_tmp; + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + + if (!fu_util_is_interesting_device (dev)) + continue; + /* only show stuff that has metadata available */ + if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + + device_id = fu_device_get_id (dev); + rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local); + if (rels == NULL) { + g_printerr ("%s\n", error_local->message); + continue; + } + + rel = g_ptr_array_index (rels, 0); + uri_tmp = fwupd_release_get_uri (rel); + remote_id = fwupd_release_get_remote_id (rel); + if (remote_id != NULL) { + FwupdRemote *remote; + g_auto(GStrv) argv = NULL; + + remote = fu_engine_get_remote_by_id (priv->engine, + remote_id, + &error_local); + if (remote == NULL) { + g_printerr ("%s\n", error_local->message); + continue; + } + + argv = g_new0 (gchar *, 2); + /* local remotes have the firmware already */ + if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) { + const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); + g_autofree gchar *path = g_path_get_dirname (fn_cache); + argv[0] = g_build_filename (path, uri_tmp, NULL); + } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + argv[0] = g_strdup (uri_tmp + 7); + /* web remote, fu_util_install will download file */ + } else { + argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error); + } + if (!fu_util_install (priv, argv, &error_local)) { + g_printerr ("%s\n", error_local->message); + continue; + } + fu_util_display_current_message (priv); + } + } + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug ("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } static gboolean @@ -906,6 +1097,94 @@ fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *script_fn = "startup.sh"; + const gchar *output_fn = "firmware.bin"; + g_autoptr(GBytes) archive_blob = NULL; + g_autoptr(GBytes) firmware_blob = NULL; + if (g_strv_length (values) < 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + archive_blob = fu_common_get_contents_bytes (values[0], error); + if (archive_blob == NULL) + return FALSE; + if (g_strv_length (values) > 2) + script_fn = values[2]; + if (g_strv_length (values) > 3) + output_fn = values[3]; + firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error); + if (firmware_blob == NULL) + return FALSE; + return fu_common_set_contents_bytes (values[1], firmware_blob, error); +} + +static void +fu_util_device_added_cb (FwupdClient *client, + FwupdDevice *device, + gpointer user_data) +{ + g_autofree gchar *tmp = fwupd_device_to_string (device); + /* TRANSLATORS: this is when a device is hotplugged */ + g_print ("%s\n%s", _("Device added:"), tmp); +} + +static void +fu_util_device_removed_cb (FwupdClient *client, + FwupdDevice *device, + gpointer user_data) +{ + g_autofree gchar *tmp = fwupd_device_to_string (device); + /* TRANSLATORS: this is when a device is hotplugged */ + g_print ("%s\n%s", _("Device removed:"), tmp); +} + +static void +fu_util_device_changed_cb (FwupdClient *client, + FwupdDevice *device, + gpointer user_data) +{ + g_autofree gchar *tmp = fwupd_device_to_string (device); + /* TRANSLATORS: this is when a device has been updated */ + g_print ("%s\n%s", _("Device changed:"), tmp); +} + +static void +fu_util_changed_cb (FwupdClient *client, gpointer user_data) +{ + /* TRANSLATORS: this is when the daemon state changes */ + g_print ("%s\n", _("Changed")); +} + +static gboolean +fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdClient) client = fwupd_client_new (); + + /* get all the devices */ + if (!fwupd_client_connect (client, priv->cancellable, error)) + return FALSE; + + /* watch for any hotplugged device */ + g_signal_connect (client, "changed", + G_CALLBACK (fu_util_changed_cb), priv); + g_signal_connect (client, "device-added", + G_CALLBACK (fu_util_device_added_cb), priv); + g_signal_connect (client, "device-removed", + G_CALLBACK (fu_util_device_removed_cb), priv); + g_signal_connect (client, "device-changed", + G_CALLBACK (fu_util_device_changed_cb), priv); + g_signal_connect (priv->cancellable, "cancelled", + G_CALLBACK (fu_util_cancelled_cb), priv); + g_main_loop_run (priv->loop); + return TRUE; +} + int main (int argc, char *argv[]) { @@ -931,6 +1210,9 @@ main (int argc, char *argv[]) { "force", '\0', 0, G_OPTION_ARG_NONE, &force, /* TRANSLATORS: command line option */ _("Override plugin warning"), NULL }, + { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check, + /* TRANSLATORS: command line option */ + _("Do not check for reboot after update"), NULL }, { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices, /* TRANSLATORS: command line option */ _("Show devices that are not updatable"), NULL }, @@ -957,6 +1239,12 @@ main (int argc, char *argv[]) /* add commands */ priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_item_free); + fu_util_add (priv->cmd_array, + "build-firmware", + "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]", + /* TRANSLATORS: command description */ + _("Build firmware using a sandbox"), + fu_util_firmware_builder); fu_util_add (priv->cmd_array, "smbios-dump", "FILE", @@ -975,6 +1263,12 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Gets details about a firmware file"), fu_util_get_details); + fu_util_add (priv->cmd_array, + "get-updates", + NULL, + /* TRANSLATORS: command description */ + _("Gets the list of updates for connected hardware"), + fu_util_get_updates); fu_util_add (priv->cmd_array, "get-devices", NULL, @@ -1023,6 +1317,18 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Return all the hardware IDs for the machine"), fu_util_hwids); + fu_util_add (priv->cmd_array, + "monitor", + NULL, + /* TRANSLATORS: command description */ + _("Monitor the daemon for events"), + fu_util_monitor); + fu_util_add (priv->cmd_array, + "update", + NULL, + /* TRANSLATORS: command description */ + _("Update all devices that match local metadata"), + fu_util_update); /* do stuff on ctrl+c */ priv->cancellable = g_cancellable_new (); @@ -1036,6 +1342,12 @@ main (int argc, char *argv[]) g_ptr_array_sort (priv->cmd_array, (GCompareFunc) fu_sort_command_name_cb); + /* non-TTY consoles cannot answer questions */ + if (isatty (fileno (stdout)) == 0) { + priv->no_reboot_check = TRUE; + fu_progressbar_set_interactive (priv->progressbar, FALSE); + } + /* get a list of the commands */ priv->context = g_option_context_new (NULL); cmd_descriptions = fu_util_get_descriptions (priv->cmd_array); diff --git a/src/fu-udev-device.c b/src/fu-udev-device.c index db726bb7d..5c7e4db80 100644 --- a/src/fu-udev-device.c +++ b/src/fu-udev-device.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2018 Richard Hughes + * Copyright (C) 2017-2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -83,6 +83,32 @@ fu_udev_device_read_uint16 (const gchar *str) return (guint16) g_ascii_strtoull (buf, NULL, 16); } +static void +fu_udev_device_dump_internal (GUdevDevice *udev_device) +{ +#ifdef HAVE_GUDEV_232 + const gchar * const *keys; + + keys = g_udev_device_get_property_keys (udev_device); + for (guint i = 0; keys[i] != NULL; i++) { + g_debug ("%s={%s}", keys[i], + g_udev_device_get_property (udev_device, keys[i])); + } + keys = g_udev_device_get_sysfs_attr_keys (udev_device); + for (guint i = 0; keys[i] != NULL; i++) { + g_debug ("%s=[%s]", keys[i], + g_udev_device_get_sysfs_attr (udev_device, keys[i])); + } +#endif +} + +void +fu_udev_device_dump (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + fu_udev_device_dump_internal (priv->udev_device); +} + static gboolean fu_udev_device_probe (FuDevice *device, GError **error) { @@ -91,6 +117,7 @@ fu_udev_device_probe (FuDevice *device, GError **error) FuUdevDevicePrivate *priv = GET_PRIVATE (self); const gchar *tmp; g_autofree gchar *subsystem = NULL; + g_autoptr(GUdevDevice) udev_parent = NULL; /* set ven:dev:rev */ priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "vendor"); @@ -98,17 +125,18 @@ fu_udev_device_probe (FuDevice *device, GError **error) priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "revision"); /* fallback to the parent */ - if (priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0) { - g_autoptr(GUdevDevice) parent = g_udev_device_get_parent (priv->udev_device); - priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (parent, "vendor"); - priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (parent, "device"); - priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (parent, "revision"); + udev_parent = g_udev_device_get_parent (priv->udev_device); + if (udev_parent != NULL && + priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0) { + priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "vendor"); + priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "device"); + priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "revision"); } /* hidraw helpfully encodes the information in a different place */ - if (priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0 && + if (udev_parent != NULL && + priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0 && g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), "hidraw") == 0) { - g_autoptr(GUdevDevice) udev_parent = g_udev_device_get_parent (priv->udev_device); tmp = g_udev_device_get_property (udev_parent, "HID_ID"); if (tmp != NULL && strlen (tmp) == 22) { priv->vendor = fu_udev_device_read_uint16 (tmp + 10); @@ -139,6 +167,8 @@ fu_udev_device_probe (FuDevice *device, GError **error) tmp = g_udev_device_get_property (priv->udev_device, "FWUPD_MODEL"); if (tmp == NULL) tmp = g_udev_device_get_property (priv->udev_device, "ID_MODEL_FROM_DATABASE"); + if (tmp == NULL) + tmp = g_udev_device_get_property (priv->udev_device, "ID_MODEL"); if (tmp != NULL) fu_device_set_name (device, tmp); } @@ -148,10 +178,28 @@ fu_udev_device_probe (FuDevice *device, GError **error) tmp = g_udev_device_get_property (priv->udev_device, "FWUPD_VENDOR"); if (tmp == NULL) tmp = g_udev_device_get_property (priv->udev_device, "ID_VENDOR_FROM_DATABASE"); + if (tmp == NULL) + tmp = g_udev_device_get_property (priv->udev_device, "ID_VENDOR"); if (tmp != NULL) fu_device_set_vendor (device, tmp); } + /* set serial */ + if (fu_device_get_serial (device) == NULL) { + tmp = g_udev_device_get_property (priv->udev_device, "ID_SERIAL_SHORT"); + if (tmp == NULL) + tmp = g_udev_device_get_property (priv->udev_device, "ID_SERIAL"); + if (tmp != NULL) + fu_device_set_serial (device, tmp); + } + + /* set revision */ + if (fu_device_get_version (device) == NULL) { + tmp = g_udev_device_get_property (priv->udev_device, "ID_REVISION"); + if (tmp != NULL) + fu_device_set_version (device, tmp); + } + /* set vendor ID */ subsystem = g_ascii_strup (fu_udev_device_get_subsystem (self), -1); if (subsystem != NULL && priv->vendor != 0x0000) { @@ -203,6 +251,24 @@ fu_udev_device_set_dev (FuUdevDevice *self, GUdevDevice *udev_device) return; } +guint +fu_udev_device_get_slot_depth (FuUdevDevice *self, const gchar *subsystem) +{ + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (self)); + g_autoptr(GUdevDevice) device_tmp = NULL; + + device_tmp = g_udev_device_get_parent_with_subsystem (udev_device, subsystem, NULL); + if (device_tmp == NULL) + return 0; + for (guint i = 0; i < 0xff; i++) { + g_autoptr(GUdevDevice) parent = g_udev_device_get_parent (device_tmp); + if (parent == NULL) + return i; + g_set_object (&device_tmp, parent); + } + return 0; +} + static void fu_udev_device_incorporate (FuDevice *self, FuDevice *donor) { @@ -320,6 +386,29 @@ fu_udev_device_get_revision (FuUdevDevice *self) return priv->revision; } +static GString * +fu_udev_device_get_parent_subsystems (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + GString *str = g_string_new (NULL); + g_autoptr(GUdevDevice) udev_device = g_object_ref (priv->udev_device); + + /* find subsystems of all parent devices */ + while (TRUE) { + g_autoptr(GUdevDevice) parent = g_udev_device_get_parent (udev_device); + if (parent == NULL) + break; + if (g_udev_device_get_subsystem (parent) != NULL) { + g_string_append_printf (str, "%s,", + g_udev_device_get_subsystem (parent)); + } + g_set_object (&udev_device, g_steal_pointer (&parent)); + } + if (str->len > 0) + g_string_truncate (str, str->len - 1); + return str; +} + /** * fu_udev_device_set_physical_id: * @self: A #GUdevDevice @@ -352,11 +441,13 @@ fu_udev_device_set_physical_id (FuUdevDevice *self, const gchar *subsystem, GErr udev_device = g_udev_device_get_parent_with_subsystem (priv->udev_device, subsystem, NULL); if (udev_device == NULL) { + g_autoptr(GString) str = NULL; + str = fu_udev_device_get_parent_subsystems (self); g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "failed to find device with subsystem %s", - subsystem); + "failed to find device with subsystem %s, only got %s", + subsystem, str->str); return FALSE; } } @@ -370,7 +461,8 @@ fu_udev_device_set_physical_id (FuUdevDevice *self, const gchar *subsystem, GErr return FALSE; } physical_id = g_strdup_printf ("PCI_SLOT_NAME=%s", tmp); - } else if (g_strcmp0 (subsystem, "usb") == 0) { + } else if (g_strcmp0 (subsystem, "usb") == 0 || + g_strcmp0 (subsystem, "scsi") == 0) { tmp = g_udev_device_get_property (udev_device, "DEVPATH"); if (tmp == NULL) { g_set_error_literal (error, diff --git a/src/fu-udev-device.h b/src/fu-udev-device.h index ca72f3309..efb76b9f9 100644 --- a/src/fu-udev-device.h +++ b/src/fu-udev-device.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2018 Richard Hughes + * Copyright (C) 2017-2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -32,9 +32,12 @@ const gchar *fu_udev_device_get_subsystem (FuUdevDevice *self); guint16 fu_udev_device_get_vendor (FuUdevDevice *self); guint16 fu_udev_device_get_model (FuUdevDevice *self); guint8 fu_udev_device_get_revision (FuUdevDevice *self); +guint fu_udev_device_get_slot_depth (FuUdevDevice *self, + const gchar *subsystem); gboolean fu_udev_device_set_physical_id (FuUdevDevice *self, const gchar *subsystem, GError **error); +void fu_udev_device_dump (FuUdevDevice *self); G_END_DECLS diff --git a/src/fu-usb-device.c b/src/fu-usb-device.c index bd455c6df..996ef4459 100644 --- a/src/fu-usb-device.c +++ b/src/fu-usb-device.c @@ -8,8 +8,6 @@ #include "config.h" -#include - #include "fu-usb-device-private.h" /** @@ -230,6 +228,7 @@ fu_usb_device_probe (FuDevice *device, GError **error) g_autofree gchar *devid1 = NULL; g_autofree gchar *devid2 = NULL; g_autofree gchar *vendor_id = NULL; + g_autoptr(GPtrArray) intfs = NULL; /* set vendor ID */ vendor_id = g_strdup_printf ("USB:0x%04X", g_usb_device_get_vid (priv->usb_device)); @@ -238,8 +237,8 @@ fu_usb_device_probe (FuDevice *device, GError **error) /* set the version if the release has been set */ release = g_usb_device_get_release (priv->usb_device); if (release != 0x0) { - g_autofree gchar *version = as_utils_version_from_uint16 (release, - AS_VERSION_PARSE_FLAG_USE_BCD); + g_autofree gchar *version = NULL; + version = fu_common_version_from_uint16 (release, FU_VERSION_FORMAT_BCD); fu_device_set_version (device, version); } @@ -257,6 +256,29 @@ fu_usb_device_probe (FuDevice *device, GError **error) g_usb_device_get_vid (priv->usb_device)); fu_device_add_guid (device, devid0); + /* add the interface GUIDs */ + intfs = g_usb_device_get_interfaces (priv->usb_device, error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index (intfs, i); + g_autofree gchar *intid1 = NULL; + g_autofree gchar *intid2 = NULL; + g_autofree gchar *intid3 = NULL; + intid1 = g_strdup_printf ("USB\\CLASS_%02X&SUBCLASS_%02X&PROT_%02X", + g_usb_interface_get_class (intf), + g_usb_interface_get_subclass (intf), + g_usb_interface_get_protocol (intf)); + fu_device_add_guid (device, intid1); + intid2 = g_strdup_printf ("USB\\CLASS_%02X&SUBCLASS_%02X", + g_usb_interface_get_class (intf), + g_usb_interface_get_subclass (intf)); + fu_device_add_guid (device, intid2); + intid3 = g_strdup_printf ("USB\\CLASS_%02X", + g_usb_interface_get_class (intf)); + fu_device_add_guid (device, intid3); + } + /* subclassed */ if (klass->probe != NULL) { if (!klass->probe (self, error)) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 249088e13..cdeb2ff6b 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -6,7 +6,6 @@ #include -#include #include #include #include @@ -162,11 +161,6 @@ fu_util_get_versions (void) #endif g_string_append_printf (string, "compile-time dependency versions\n"); - g_string_append_printf (string, - "\tappstream-glib:\t%d.%d.%d\n", - AS_MAJOR_VERSION, - AS_MINOR_VERSION, - AS_MICRO_VERSION); g_string_append_printf (string, "\tgusb:\t%d.%d.%d\n", G_USB_MAJOR_VERSION, @@ -179,3 +173,124 @@ fu_util_get_versions (void) #endif return g_string_free (string, FALSE); } + +static gboolean +fu_util_update_shutdown (GError **error) +{ + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GVariant) val = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) + return FALSE; + +#ifdef HAVE_SYSTEMD + /* shutdown using logind */ + val = g_dbus_connection_call_sync (connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "PowerOff", + g_variant_new ("(b)", TRUE), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); +#elif defined(HAVE_CONSOLEKIT) + /* shutdown using ConsoleKit */ + val = g_dbus_connection_call_sync (connection, + "org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + "Stop", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "No supported backend compiled in to perform the operation."); +#endif + return val != NULL; +} + +static gboolean +fu_util_update_reboot (GError **error) +{ + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GVariant) val = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) + return FALSE; + +#ifdef HAVE_SYSTEMD + /* reboot using logind */ + val = g_dbus_connection_call_sync (connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Reboot", + g_variant_new ("(b)", TRUE), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); +#elif defined(HAVE_CONSOLEKIT) + /* reboot using ConsoleKit */ + val = g_dbus_connection_call_sync (connection, + "org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + "Restart", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "No supported backend compiled in to perform the operation."); +#endif + return val != NULL; +} + +gboolean +fu_util_prompt_complete (FwupdDeviceFlags flags, gboolean prompt, GError **error) +{ + if (flags & FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { + if (prompt) { + g_print ("\n%s %s [Y|n]: ", + /* TRANSLATORS: explain why we want to shutdown */ + _("An update requires the system to shutdown to complete."), + /* TRANSLATORS: shutdown to apply the update */ + _("Shutdown now?")); + if (!fu_util_prompt_for_boolean (TRUE)) + return TRUE; + } + return fu_util_update_shutdown (error); + } + if (flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { + if (prompt) { + g_print ("\n%s %s [Y|n]: ", + /* TRANSLATORS: explain why we want to reboot */ + _("An update requires a reboot to complete."), + /* TRANSLATORS: reboot to apply the update */ + _("Restart now?")); + if (!fu_util_prompt_for_boolean (TRUE)) + return TRUE; + } + return fu_util_update_reboot (error); + } + + return TRUE; +} diff --git a/src/fu-util-common.h b/src/fu-util-common.h index ac2664c35..d099ec409 100644 --- a/src/fu-util-common.h +++ b/src/fu-util-common.h @@ -10,6 +10,9 @@ #include #include +/* this is only valid for tools */ +#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1) + void fu_util_print_data (const gchar *title, const gchar *msg); guint fu_util_prompt_for_number (guint maxnum); @@ -21,4 +24,8 @@ gchar *fu_util_get_user_cache_path (const gchar *fn); gchar *fu_util_get_versions (void); +gboolean fu_util_prompt_complete (FwupdDeviceFlags flags, + gboolean prompt, + GError **error); + #endif /* __FU_UTIL_COMMON_H__ */ diff --git a/src/fu-util.c b/src/fu-util.c index e8270af53..a8427d0c6 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -9,7 +9,7 @@ #include "config.h" #include -#include +#include #include #include #include @@ -29,9 +29,6 @@ #include "fu-util-common.h" #include "fwupd-common-private.h" -/* this is only valid in this file */ -#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1) - /* custom return code */ #define EXIT_NOTHING_TO_DO 2 @@ -60,6 +57,8 @@ typedef struct { /* only valid in update and downgrade */ FuUtilOperation current_operation; FwupdDevice *current_device; + gchar *current_message; + FwupdDeviceFlags completion_flags; } FuUtilPrivate; typedef gboolean (*FuUtilPrivateCb) (FuUtilPrivate *util, @@ -230,6 +229,17 @@ fu_util_update_device_changed_cb (FwupdClient *client, g_warning ("no FuUtilOperation set"); } g_set_object (&priv->current_device, device); + + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + else if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + if (priv->current_message == NULL) { + const gchar *tmp = fwupd_device_get_update_message (priv->current_device); + if (tmp != NULL) + priv->current_message = g_strdup (tmp); + } } static FwupdDevice * @@ -439,6 +449,60 @@ fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error) return fu_util_report_history (priv, NULL, error); } +static gchar * +fu_util_convert_appstream_description (const gchar *xml, GError **error) +{ + g_autoptr(GString) str = g_string_new (NULL); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* parse XML */ + silo = xb_silo_new_from_xml (xml, error); + if (silo == NULL) + return NULL; + + n = xb_silo_get_root (silo); + while (n != NULL) { + g_autoptr(XbNode) n2 = NULL; + + /* support

,

    ,
      and
    1. , ignore all else */ + if (g_strcmp0 (xb_node_get_element (n), "p") == 0) { + g_string_append_printf (str, "%s\n\n", xb_node_get_text (n)); + } else if (g_strcmp0 (xb_node_get_element (n), "ul") == 0) { + g_autoptr(GPtrArray) children = xb_node_get_children (n); + for (guint i = 0; i < children->len; i++) { + XbNode *nc = g_ptr_array_index (children, i); + if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { + g_string_append_printf (str, " • %s\n", + xb_node_get_text (nc)); + } + } + g_string_append (str, "\n"); + } else if (g_strcmp0 (xb_node_get_element (n), "ol") == 0) { + g_autoptr(GPtrArray) children = xb_node_get_children (n); + for (guint i = 0; i < children->len; i++) { + XbNode *nc = g_ptr_array_index (children, i); + if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { + g_string_append_printf (str, " %u. %s\n", + i + 1, + xb_node_get_text (nc)); + } + } + g_string_append (str, "\n"); + } + + n2 = xb_node_get_next (n); + g_set_object (&n, n2); + } + + /* remove extra newline */ + if (str->len > 0) + g_string_truncate (str, str->len - 1); + + /* success */ + return g_string_free (g_steal_pointer (&str), FALSE); +} + static gboolean fu_util_modify_remote_warning (FuUtilPrivate *priv, FwupdRemote *remote, GError **error) { @@ -449,7 +513,7 @@ fu_util_modify_remote_warning (FuUtilPrivate *priv, FwupdRemote *remote, GError warning_markup = fwupd_remote_get_agreement (remote); if (warning_markup == NULL) return TRUE; - warning_plain = as_markup_convert_simple (warning_markup, error); + warning_plain = fu_util_convert_appstream_description (warning_markup, error); if (warning_plain == NULL) return FALSE; @@ -590,6 +654,15 @@ fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GErro return g_steal_pointer (&filename); } +static void +fu_util_display_current_message (FuUtilPrivate *priv) +{ + if (priv->current_message == NULL) + return; + g_print ("%s\n", priv->current_message); + g_clear_pointer (&priv->current_message, g_free); +} + static gboolean fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -617,7 +690,20 @@ fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) filename = fu_util_download_if_required (priv, values[0], error); if (filename == NULL) return FALSE; - return fwupd_client_install (priv->client, id, filename, priv->flags, NULL, error); + + if (!fwupd_client_install (priv->client, id, filename, priv->flags, NULL, error)) + return FALSE; + + fu_util_display_current_message (priv); + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug ("skipping reboot check"); + return TRUE; + } + + /* show reboot if needed */ + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } static gboolean @@ -645,51 +731,6 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } -static gboolean -fu_util_update_reboot (GError **error) -{ - g_autoptr(GDBusConnection) connection = NULL; - g_autoptr(GVariant) val = NULL; - - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); - if (connection == NULL) - return FALSE; - -#ifdef HAVE_SYSTEMD - /* reboot using logind */ - val = g_dbus_connection_call_sync (connection, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "Reboot", - g_variant_new ("(b)", TRUE), - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - error); -#elif defined(HAVE_CONSOLEKIT) - /* reboot using ConsoleKit */ - val = g_dbus_connection_call_sync (connection, - "org.freedesktop.ConsoleKit", - "/org/freedesktop/ConsoleKit/Manager", - "org.freedesktop.ConsoleKit.Manager", - "Restart", - NULL, - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - error); -#else - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "No supported backend compiled in to perform the operation."); -#endif - return val != NULL; -} - static gboolean fu_util_install_prepared (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -746,8 +787,8 @@ fu_util_install_prepared (FuUtilPrivate *priv, gchar **values, GError **error) continue; /* tell the user what's going to happen */ - vercmp = as_utils_vercmp (fwupd_device_get_version (dev), - fwupd_release_get_version (rel)); + vercmp = fu_common_vercmp (fwupd_device_get_version (dev), + fwupd_release_get_version (rel)); if (vercmp == 0) { /* TRANSLATORS: the first replacement is a display name * e.g. "ColorHugALS" and the second is a version number @@ -792,7 +833,7 @@ fu_util_install_prepared (FuUtilPrivate *priv, gchar **values, GError **error) } /* reboot */ - if (!fu_util_update_reboot (error)) + if (!fu_util_prompt_complete (FWUPD_DEVICE_FLAG_NEEDS_REBOOT, FALSE, error)) return FALSE; g_print ("%s\n", _("Done!")); @@ -1484,7 +1525,7 @@ fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error) tmp = fwupd_release_get_description (rel); if (tmp != NULL) { g_autofree gchar *desc = NULL; - desc = as_markup_convert_simple (tmp, NULL); + desc = fu_util_convert_appstream_description (tmp, NULL); /* TRANSLATORS: section header for firmware description */ fu_util_print_data (_("Description"), desc); } @@ -1649,6 +1690,45 @@ fu_util_perhaps_refresh_remotes (FuUtilPrivate *priv, GError **error) return fu_util_download_metadata (priv, error); } +static gchar * +fu_util_time_to_str (guint64 tmp) +{ + g_return_val_if_fail (tmp != 0, FALSE); + + /* seconds */ + if (tmp < 60) { + /* TRANSLATORS: duration in seconds */ + return g_strdup_printf (ngettext ("%u second", "%u seconds", + (gint) tmp), + (guint) tmp); + } + + /* minutes */ + tmp /= 60; + if (tmp < 60) { + /* TRANSLATORS: duration in minutes */ + return g_strdup_printf (ngettext ("%u minute", "%u minutes", + (gint) tmp), + (guint) tmp); + } + + /* hours */ + tmp /= 60; + if (tmp < 60) { + /* TRANSLATORS: duration in minutes */ + return g_strdup_printf (ngettext ("%u hour", "%u hours", + (gint) tmp), + (guint) tmp); + } + + /* days */ + tmp /= 24; + /* TRANSLATORS: duration in days! */ + return g_strdup_printf (ngettext ("%u day", "%u days", + (gint) tmp), + (guint) tmp); +} + static gboolean fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1696,6 +1776,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) /* print all releases */ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index (rels, j); + guint64 duration; GPtrArray *checksums; /* TRANSLATORS: Appstream ID for the hardware type */ @@ -1715,6 +1796,15 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) fu_util_print_data (_("Update Remote ID"), fwupd_release_get_remote_id (rel)); + /* optional approximate duration */ + duration = fwupd_release_get_install_duration (rel); + if (duration > 0) { + g_autofree gchar *str = fu_util_time_to_str (duration); + /* TRANSLATORS: section header for the amount + * of time it takes to install the update */ + fu_util_print_data (_("Update Duration"), str); + } + checksums = fwupd_release_get_checksums (rel); for (guint k = 0; k < checksums->len; k++) { const gchar *checksum = g_ptr_array_index (checksums, k); @@ -1731,9 +1821,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) tmp = fwupd_release_get_description (rel); if (tmp != NULL) { g_autofree gchar *md = NULL; - md = as_markup_convert (tmp, - AS_MARKUP_CONVERT_FORMAT_SIMPLE, - NULL); + md = fu_util_convert_appstream_description (tmp, NULL); if (md != NULL) { /* TRANSLATORS: section header for long firmware desc */ fu_util_print_data (_("Update Description"), md); @@ -1876,101 +1964,6 @@ fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } -static void -fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data) -{ - FuUtilPrivate *priv = (FuUtilPrivate *) user_data; - /* TRANSLATORS: this is when a device ctrl+c's a watch */ - g_print ("%s\n", _("Cancelled")); - g_main_loop_quit (priv->loop); -} - -static void -fu_util_device_added_cb (FwupdClient *client, - FwupdDevice *device, - gpointer user_data) -{ - g_autofree gchar *tmp = fwupd_device_to_string (device); - /* TRANSLATORS: this is when a device is hotplugged */ - g_print ("%s\n%s", _("Device added:"), tmp); -} - -static void -fu_util_device_removed_cb (FwupdClient *client, - FwupdDevice *device, - gpointer user_data) -{ - g_autofree gchar *tmp = fwupd_device_to_string (device); - /* TRANSLATORS: this is when a device is hotplugged */ - g_print ("%s\n%s", _("Device removed:"), tmp); -} - -static void -fu_util_device_changed_cb (FwupdClient *client, - FwupdDevice *device, - gpointer user_data) -{ - g_autofree gchar *tmp = fwupd_device_to_string (device); - /* TRANSLATORS: this is when a device has been updated */ - g_print ("%s\n%s", _("Device changed:"), tmp); -} - -static void -fu_util_changed_cb (FwupdClient *client, gpointer user_data) -{ - /* TRANSLATORS: this is when the daemon state changes */ - g_print ("%s\n", _("Changed")); -} - -static gboolean -fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error) -{ - const gchar *script_fn = "startup.sh"; - const gchar *output_fn = "firmware.bin"; - g_autoptr(GBytes) archive_blob = NULL; - g_autoptr(GBytes) firmware_blob = NULL; - if (g_strv_length (values) < 2) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "Invalid arguments"); - return FALSE; - } - archive_blob = fu_common_get_contents_bytes (values[0], error); - if (archive_blob == NULL) - return FALSE; - if (g_strv_length (values) > 2) - script_fn = values[2]; - if (g_strv_length (values) > 3) - output_fn = values[3]; - firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error); - if (firmware_blob == NULL) - return FALSE; - return fu_common_set_contents_bytes (values[1], firmware_blob, error); -} - -static gboolean -fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error) -{ - /* get all the devices */ - if (!fwupd_client_connect (priv->client, priv->cancellable, error)) - return FALSE; - - /* watch for any hotplugged device */ - g_signal_connect (priv->client, "changed", - G_CALLBACK (fu_util_changed_cb), priv); - g_signal_connect (priv->client, "device-added", - G_CALLBACK (fu_util_device_added_cb), priv); - g_signal_connect (priv->client, "device-removed", - G_CALLBACK (fu_util_device_removed_cb), priv); - g_signal_connect (priv->client, "device-changed", - G_CALLBACK (fu_util_device_changed_cb), priv); - g_signal_connect (priv->cancellable, "cancelled", - G_CALLBACK (fu_util_cancelled_cb), priv); - g_main_loop_run (priv->loop); - return TRUE; -} - static gboolean fu_util_update_device_with_release (FuUtilPrivate *priv, FwupdDevice *dev, @@ -1996,13 +1989,17 @@ fu_util_update_device_with_release (FuUtilPrivate *priv, if (remote == NULL) return FALSE; - /* local remotes have the firmware already */ + /* local and directory remotes have the firmware already */ if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) { const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); g_autofree gchar *path = g_path_get_dirname (fn_cache); - /* install with flags chosen by the user */ fn = g_build_filename (path, uri_tmp, NULL); + } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + fn = g_strdup (uri_tmp + 7); + } + /* install with flags chosen by the user */ + if (fn != NULL) { return fwupd_client_install (priv->client, fwupd_device_get_id (dev), fn, priv->flags, NULL, error); @@ -2036,7 +2033,6 @@ fu_util_update_device_with_release (FuUtilPrivate *priv, static gboolean fu_util_update_all (FuUtilPrivate *priv, GError **error) { - gboolean requires_reboot = FALSE; g_autoptr(GPtrArray) devices = NULL; /* get devices from daemon */ @@ -2067,8 +2063,8 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) rel = g_ptr_array_index (rels, 0); if (!fu_util_update_device_with_release (priv, dev, rel, error)) return FALSE; - if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) - requires_reboot = TRUE; + + fu_util_display_current_message (priv); } /* we don't want to ask anything */ @@ -2077,18 +2073,7 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) return TRUE; } - /* at least one of the updates needed a reboot */ - if (requires_reboot) { - g_print ("\n%s %s [Y|n]: ", - /* TRANSLATORS: explain why we want to upload */ - _("An update requires a reboot to complete."), - /* TRANSLATORS: reboot to apply the update */ - _("Restart now?")); - if (!fu_util_prompt_for_boolean (TRUE)) - return TRUE; - return fu_util_update_reboot (error); - } - return TRUE; + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } static gboolean @@ -2118,6 +2103,8 @@ fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **erro if (!fu_util_update_device_with_release (priv, dev, rel, error)) return FALSE; + fu_util_display_current_message (priv); + /* we don't want to ask anything */ if (priv->no_reboot_check) { g_debug ("skipping reboot check"); @@ -2126,14 +2113,9 @@ fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **erro /* the update needs the user to restart the computer */ if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) { - g_print ("\n%s %s [Y|n]: ", - /* TRANSLATORS: exactly one update needs this */ - _("The update requires a reboot to complete."), - /* TRANSLATORS: reboot to apply the update */ - _("Restart now?")); - if (!fu_util_prompt_for_boolean (TRUE)) - return TRUE; - return fu_util_update_reboot (error); + if (!fu_util_prompt_complete (FWUPD_DEVICE_FLAG_NEEDS_REBOOT, TRUE, + error)) + return FALSE; } return TRUE; } @@ -2248,6 +2230,7 @@ fu_util_private_free (FuUtilPrivate *priv) g_object_unref (priv->current_device); if (priv->soup_session != NULL) g_object_unref (priv->soup_session); + g_free (priv->current_message); g_main_loop_unref (priv->loop); g_object_unref (priv->cancellable); g_object_unref (priv->progressbar); @@ -2449,18 +2432,6 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Update the stored metadata with current ROM contents"), fu_util_verify_update); - fu_util_add (priv->cmd_array, - "monitor", - NULL, - /* TRANSLATORS: command description */ - _("Monitor the daemon for events"), - fu_util_monitor); - fu_util_add (priv->cmd_array, - "build-firmware", - "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]", - /* TRANSLATORS: command description */ - _("Build firmware using a sandbox"), - fu_util_firmware_builder); fu_util_add (priv->cmd_array, "modify-remote", "REMOTE-ID KEY VALUE", @@ -2495,6 +2466,7 @@ main (int argc, char *argv[]) priv->no_unreported_check = TRUE; priv->no_metadata_check = TRUE; priv->no_reboot_check = TRUE; + fu_progressbar_set_interactive (priv->progressbar, FALSE); } /* get a list of the commands */ @@ -2558,6 +2530,17 @@ main (int argc, char *argv[]) return EXIT_SUCCESS; } + /* show a warning if the daemon is tainted */ + if (!fwupd_client_connect (priv->client, priv->cancellable, &error)) { + g_printerr ("Failed to connect to daemon: %s\n", + error->message); + return EXIT_FAILURE; + } + if (fwupd_client_get_tainted (priv->client)) { + g_printerr ("WARNING: The daemon has loaded 3rd party code and " + "is no longer supported by the upstream developers!\n"); + } + /* run the specified command */ ret = fu_util_run (priv, argv[1], (gchar**) &argv[2], &error); if (!ret) { diff --git a/src/meson.build b/src/meson.build index 61a1db6c4..9b924ec0a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -25,12 +25,16 @@ endif libfwupdprivate = static_library( 'fwupdprivate', sources : [ + 'fu-archive.c', 'fu-common.c', + 'fu-common-guid.c', + 'fu-common-version.c', 'fu-chunk.c', 'fu-device.c', 'fu-device-locker.c', 'fu-hwids.c', 'fu-history.c', + 'fu-io-channel.c', 'fu-mutex.c', 'fu-plugin.c', 'fu-progressbar.c', @@ -45,14 +49,18 @@ libfwupdprivate = static_library( include_directories('../libfwupd'), ], dependencies : [ - appstream_glib, giounix, gudev, gusb, soup, sqlite, libarchive, + libxmlb, valgrind, + uuid, + ], + link_with : [ + fwupd, ], c_args : [ '-DFU_OFFLINE_DESTDIR=""', @@ -71,7 +79,7 @@ fwupdmgr = executable( include_directories('../libfwupd'), ], dependencies : [ - appstream_glib, + libxmlb, giounix, gudev, gusb, @@ -99,15 +107,28 @@ resources_src = gnome.compile_resources( c_name : 'fu' ) +fu_hash = custom_target( + 'fu-hash.h', + input : libfwupdprivate, + output : 'fu-hash.h', + command : [python3.path(), + join_paths(meson.current_source_dir(), 'fu-hash.py'), + '@INPUT@', '@OUTPUT@'] +) + fwupdtool = executable( 'fwupdtool', resources_src, + fu_hash, sources : [ 'fu-tool.c', keyring_src, + 'fu-archive.c', 'fu-chunk.c', 'fu-common.c', 'fu-common-cab.c', + 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-keyring.c', 'fu-keyring-result.c', @@ -117,7 +138,9 @@ fwupdtool = executable( 'fu-device.c', 'fu-device-list.c', 'fu-device-locker.c', + 'fu-idle.c', 'fu-install-task.c', + 'fu-io-channel.c', 'fu-keyring.c', 'fu-keyring-utils.c', 'fu-history.c', @@ -135,7 +158,7 @@ fwupdtool = executable( ], dependencies : [ keyring_deps, - appstream_glib, + libxmlb, libgcab, giounix, gmodule, @@ -145,6 +168,7 @@ fwupdtool = executable( sqlite, valgrind, libarchive, + uuid, ], link_with : [ fwupd, @@ -179,11 +203,15 @@ if get_option('daemon') executable( 'fwupd', resources_src, + fu_hash, sources : [ keyring_src, + 'fu-archive.c', 'fu-chunk.c', 'fu-common.c', 'fu-common-cab.c', + 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-keyring.c', 'fu-keyring-result.c', @@ -194,6 +222,8 @@ executable( 'fu-device.c', 'fu-device-list.c', 'fu-device-locker.c', + 'fu-idle.c', + 'fu-io-channel.c', 'fu-install-task.c', 'fu-keyring.c', 'fu-keyring-utils.c', @@ -212,7 +242,7 @@ executable( ], dependencies : [ keyring_deps, - appstream_glib, + libxmlb, libgcab, giounix, gmodule, @@ -223,6 +253,7 @@ executable( sqlite, valgrind, libarchive, + uuid, ], link_with : fwupd, c_args : [ @@ -231,6 +262,7 @@ executable( install : true, install_dir : join_paths(libexecdir, 'fwupd') ) + endif if get_option('tests') @@ -245,12 +277,16 @@ if get_option('tests') hwid_test_firmware, noreqs_test_firmware, test_deps, + fu_hash, sources : [ keyring_src, 'fu-self-test.c', + 'fu-archive.c', 'fu-chunk.c', 'fu-common.c', 'fu-common-cab.c', + 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-engine.c', 'fu-keyring.c', @@ -260,7 +296,9 @@ if get_option('tests') 'fu-device-list.c', 'fu-device-locker.c', 'fu-history.c', + 'fu-idle.c', 'fu-install-task.c', + 'fu-io-channel.c', 'fu-keyring.c', 'fu-keyring-result.c', 'fu-mutex.c', @@ -279,7 +317,7 @@ if get_option('tests') ], dependencies : [ keyring_deps, - appstream_glib, + libxmlb, libgcab, giounix, gmodule, @@ -289,6 +327,7 @@ if get_option('tests') sqlite, valgrind, libarchive, + uuid, ], link_with : [ fwupd, @@ -309,14 +348,21 @@ if get_option('introspection') gir_dep = declare_dependency(sources: gir) gnome.generate_gir(fwupd, sources : [ + 'fu-archive.c', + 'fu-archive.h', 'fu-chunk.c', 'fu-chunk.h', 'fu-common.c', + 'fu-common-guid.c', + 'fu-common-guid.h', + 'fu-common-version.c', + 'fu-common-version.h', 'fu-common.h', 'fu-device.c', 'fu-device.h', 'fu-device-locker.c', 'fu-device-locker.h', + 'fu-io-channel.c', 'fu-plugin.c', 'fu-plugin.h', 'fu-quirks.c', @@ -334,7 +380,7 @@ if get_option('introspection') include_directories('../libfwupd'), ], dependencies : [ - appstream_glib, + libxmlb, gir_dep, giounix, gusb, diff --git a/src/org.freedesktop.fwupd.xml b/src/org.freedesktop.fwupd.xml index 7d06d97f3..23ff36a5c 100644 --- a/src/org.freedesktop.fwupd.xml +++ b/src/org.freedesktop.fwupd.xml @@ -22,6 +22,17 @@ + + + + + + If the daemon has been tainted with a 3rd party plugin. + + + + + diff --git a/subprojects/.gitignore b/subprojects/.gitignore new file mode 100644 index 000000000..d8d6c81a5 --- /dev/null +++ b/subprojects/.gitignore @@ -0,0 +1 @@ +libxmlb diff --git a/subprojects/libxmlb.wrap b/subprojects/libxmlb.wrap new file mode 100644 index 000000000..4894da302 --- /dev/null +++ b/subprojects/libxmlb.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = libxmlb +url = https://github.com/hughsie/libxmlb.git +revision = 0.1.5