diff -Nru fwupd-1.0.9/.circleci/config.yml fwupd-1.2.10/.circleci/config.yml --- fwupd-1.0.9/.circleci/config.yml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/.circleci/config.yml 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,84 @@ +version: 2 +jobs: + build-s390x: + machine: + image: circleci/classic:latest + steps: + - checkout + - run: + name: "Build container" + command: OS=debian-s390x ./contrib/ci/generate_docker.py + - run: + name: "Run build script" + command: docker run --privileged -e CI=true -t -v `pwd`/dist:/build/dist fwupd-debian-s390x + + build-snap: + docker: + - image: superm1/snapcraft-edge:latest + steps: + - checkout + - run: + name: "Update apt" + command: apt update + - run: + name: "Build Snap" + command: snapcraft + - persist_to_workspace: + root: . + paths: + - "*.snap" + + publish-edge: + docker: + - image: cibuilds/snapcraft:stable + steps: + - attach_workspace: + at: . + - run: + name: "Publish to Store" + command: | + mkdir .snapcraft + echo $SNAPCRAFT_LOGIN_FILE | base64 --decode --ignore-garbage > .snapcraft/snapcraft.cfg + snapcraft push *.snap --release edge + + publish-stable: + docker: + - image: cibuilds/snapcraft:stable + steps: + - attach_workspace: + at: . + - run: + name: "Publish to Store" + command: | + mkdir .snapcraft + echo $SNAPCRAFT_LOGIN_FILE | base64 --decode --ignore-garbage > .snapcraft/snapcraft.cfg + snapcraft push *.snap --release stable + +workflows: + version: 2 + main: + jobs: + - build-s390x + - build-snap + - publish-edge: + requires: + - build-snap + filters: + branches: + only: master + deploy: + jobs: + - build-snap: + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ + - publish-stable: + requires: + - build-snap + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ diff -Nru fwupd-1.0.9/COMMITMENT fwupd-1.2.10/COMMITMENT --- fwupd-1.0.9/COMMITMENT 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/COMMITMENT 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,45 @@ +Common Cure Rights Commitment, version 1.0 + +Before filing or continuing to prosecute any legal proceeding or claim +(other than a Defensive Action) arising from termination of a Covered +License, we commit to extend to the person or entity ('you') accused +of violating the Covered License the following provisions regarding +cure and reinstatement, taken from GPL version 3. As used here, the +term 'this License' refers to the specific Covered License being +enforced. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly + and finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you + have received notice of violation of this License (for any work) + from that copyright holder, and you cure the violation prior to 30 + days after your receipt of the notice. + +We intend this Commitment to be irrevocable, and binding and +enforceable against us and assignees of or successors to our +copyrights. + +Definitions + +'Covered License' means the GNU General Public License, version 2 +(GPLv2), the GNU Lesser General Public License, version 2.1 +(LGPLv2.1), or the GNU Library General Public License, version 2 +(LGPLv2), all as published by the Free Software Foundation. + +'Defensive Action' means a legal proceeding or claim that We bring +against you in response to a prior proceeding or claim initiated by +you or your affiliate. + +'We' means each contributor to this repository as of the date of +inclusion of this file, including subsidiaries of a corporate +contributor. + +This work is available under a Creative Commons Attribution-ShareAlike +4.0 International license. diff -Nru fwupd-1.0.9/contrib/add-capsule-header.py fwupd-1.2.10/contrib/add-capsule-header.py --- fwupd-1.0.9/contrib/add-capsule-header.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/add-capsule-header.py 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2019 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import sys +import uuid +import argparse +import struct + +CAPSULE_FLAGS_PERSIST_ACROSS_RESET = 0x00010000 +CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE = 0x00020000 +CAPSULE_FLAGS_INITIATE_RESET = 0x00040000 + +def main(args): + + # parse GUID from command line + try: + guid = uuid.UUID(args.guid) + except ValueError as e: + print(e) + return 1 + try: + with open(args.bin, 'rb') as f: + bin_data = f.read() + except FileNotFoundError as e: + print(e) + return 1 + + # check if already has header + hdrsz = struct.calcsize('<16sIII') + if len(bin_data) >= hdrsz: + hdr = struct.unpack('<16sIII', bin_data[:hdrsz]) + imgsz = hdr[3] + if imgsz == len(bin_data): + print('Replacing existing CAPSULE_HEADER of:') + guid_mixed = uuid.UUID(bytes_le=hdr[0]) + hdrsz_old = hdr[1] + flags = hdr[2] + print('GUID: %s' % guid_mixed) + print('HdrSz: 0x%04x' % hdrsz_old) + print('Flags: 0x%04x' % flags) + print('PayloadSz: 0x%04x' % imgsz) + bin_data = bin_data[hdrsz_old:] + + # set header flags + flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET + if args.flags: + flags = int(args.flags, 16) + + # build update capsule header + imgsz = hdrsz + len(bin_data) + hdr = struct.pack('<16sIII', guid.bytes_le, hdrsz, flags, imgsz) + with open(args.cap, 'wb') as f: + f.write(hdr + bin_data) + print('Wrote capsule %s' % args.cap) + print('GUID: %s' % guid) + print('HdrSz: 0x%04x' % hdrsz) + print('Flags: 0x%04x' % flags) + print('PayloadSz: 0x%04x' % imgsz) + return 0 + +parser = argparse.ArgumentParser(description='Add capsule header on firmware') +parser.add_argument('--guid', help='GUID of the device', required=True) +parser.add_argument('--bin', help='Path to the .bin file', required=True) +parser.add_argument('--cap', help='Output capsule file path', required=True) +parser.add_argument('--flags', help='Flags, e.g. 0x40000', default=None) +args = parser.parse_args() + +sys.exit(main(args)) diff -Nru fwupd-1.0.9/contrib/ci/centos.sh fwupd-1.2.10/contrib/ci/centos.sh --- fwupd-1.0.9/contrib/ci/centos.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/centos.sh 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,10 @@ meson .. \ --werror \ -Dplugin_uefi=false \ - -Dplugin_uefi_labels=false \ - -Dplugin_dell=true \ + -Dplugin_dell=false \ + -Dplugin_modem_manager=false \ -Dplugin_synaptics=true \ + -Dplugin_flashrom=true \ -Dintrospection=true \ -Dgtkdoc=true \ -Dpkcs7=false \ diff -Nru fwupd-1.0.9/contrib/ci/check_missing_translations.sh fwupd-1.2.10/contrib/ci/check_missing_translations.sh --- fwupd-1.0.9/contrib/ci/check_missing_translations.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/check_missing_translations.sh 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +cd po +intltool-update -m +if [ -f missing ]; then + exit 1 +fi diff -Nru fwupd-1.0.9/contrib/ci/debian_s390x.sh fwupd-1.2.10/contrib/ci/debian_s390x.sh --- fwupd-1.0.9/contrib/ci/debian_s390x.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/debian_s390x.sh 2019-07-15 18:25:54.000000000 +0000 @@ -3,6 +3,12 @@ set -x export LC_ALL=C.UTF-8 + +#evaluate using Debian's build flags +eval "$(dpkg-buildflags --export=sh)" +#filter out -Bsymbolic-functions +export LDFLAGS=$(dpkg-buildflags --get LDFLAGS | sed "s/-Wl,-Bsymbolic-functions\s//") + rm -rf build mkdir -p build cp contrib/ci/s390x_cross.txt build/ @@ -10,13 +16,20 @@ meson .. \ --cross-file s390x_cross.txt \ --werror \ + -Dplugin_flashrom=false \ -Dplugin_uefi=false \ - -Dplugin_uefi_labels=false \ -Dplugin_dell=false \ - -Dplugin_synaptics=false \ + -Dplugin_modem_manager=false \ + -Dplugin_redfish=false \ -Dintrospection=false \ -Dgtkdoc=false \ + -Dlibxmlb:introspection=false \ + -Dlibxmlb:gtkdoc=false \ -Dman=false ninja -v ninja test -v cd .. + + +#test for missing translation files +./contrib/ci/check_missing_translations.sh diff -Nru fwupd-1.0.9/contrib/ci/debian.sh fwupd-1.2.10/contrib/ci/debian.sh --- fwupd-1.0.9/contrib/ci/debian.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/debian.sh 2019-07-15 18:25:54.000000000 +0000 @@ -21,7 +21,12 @@ mv contrib/debian . sed s/quilt/native/ debian/source/format -i #generate control file -./contrib/ci/generate_debian_control.py debian/control.in debian/control +./contrib/ci/generate_debian.py + +#disable unit tests if fwupd is already installed (may cause problems) +if [ -x /usr/lib/fwupd/fwupd ]; then + export DEB_BUILD_OPTIONS=nocheck +fi #build the package EDITOR=/bin/true dch --create --package fwupd -v $VERSION "CI Build" debuild --no-lintian --preserve-envvar CI --preserve-envvar CC diff -Nru fwupd-1.0.9/contrib/ci/dependencies.xml fwupd-1.2.10/contrib/ci/dependencies.xml --- fwupd-1.0.9/contrib/ci/dependencies.xml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/dependencies.xml 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,15 @@ + + + + + + + + + + @@ -13,18 +23,25 @@ - - - + + + + + - - - + - + + + + + + + + @@ -49,7 +66,12 @@ - + + + + + + @@ -120,15 +142,51 @@ - + + + libftdi + + + libftdi-devel + + + libftdi-devel + + + + + + + + + + + + pciutils + + + pciutils-devel + + + pciutils-devel + + + + + + + + + + - ttf-dejavu + noto-fonts - dejavu-sans-fonts + google-noto-sans-cjk-ttc-fonts - dejavu-sans-fonts + google-noto-sans-cjk-ttc-fonts @@ -152,14 +210,14 @@ - (>= 10.3) + (>= 11) - (>= 10.3) + (>= 11) @@ -198,6 +256,11 @@ + + + + + elfutils-libelf-devel @@ -260,37 +323,6 @@ - - - fwupdate - - - fwupdate-devel - - - fwupdate-devel - - - - (>= 10-3) - amd64 - arm64 - armhf - i386 - - - - - - - amd64 - arm64 - armhf - i386 - - - - @@ -339,13 +371,35 @@ - + + + gnu-efi-libs + + + + + + + - - + + amd64 + arm64 + armhf + i386 + + gnu-efi + gnu-efi - + + amd64 + arm64 + armhf + i386 + + gnu-efi + gnu-efi @@ -524,30 +578,34 @@ - + - appstream-glib + libxmlb + - libappstream-glib-devel + libxmlb-devel - (>= 0.7.4) + (>= 0.1.5) - libappstream-glib-dev:s390x + libxmlb-dev:s390x + @@ -568,12 +626,41 @@ + + efivar + + + efivar-devel + + + efivar-devel + + + + amd64 + arm64 + armhf + i386 + + + + + + + amd64 + arm64 + armhf + i386 + + + + + amd64 arm64 armhf - armel i386 @@ -584,13 +671,21 @@ amd64 arm64 armhf - armel i386 + + gcab + + + libgcab1-devel + + + libgcab1-devel + @@ -633,6 +728,9 @@ + + libgusb + libgusb-devel @@ -685,11 +783,17 @@ - + + i386 + amd64 + + + libsoup + libsoup-devel @@ -808,6 +912,9 @@ + + polkit + polkit @@ -829,6 +936,48 @@ + + + ModemManager-glib-devel + + + ModemManager-glib-devel + + + modemmanager + + + + + libmm-glib-dev:s390x + + + + + + + + + + libqmi-devel + + + libqmi-devel + + + libqmi + + + + + libqmi-glib-dev:s390x + + + + + + + polkit-devel @@ -979,6 +1128,27 @@ + + + + + + + + + + + + + + + + + + + + + diff -Nru fwupd-1.0.9/contrib/ci/Dockerfile-centos.in fwupd-1.2.10/contrib/ci/Dockerfile-centos.in --- fwupd-1.0.9/contrib/ci/Dockerfile-centos.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/Dockerfile-centos.in 2019-07-15 18:25:54.000000000 +0000 @@ -7,10 +7,9 @@ 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 update systemd -y +RUN yum --enablerepo=epel-testing -y update RUN mkdir /build WORKDIR /build COPY . . diff -Nru fwupd-1.0.9/contrib/ci/Dockerfile-debian.in fwupd-1.2.10/contrib/ci/Dockerfile-debian.in --- fwupd-1.0.9/contrib/ci/Dockerfile-debian.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/Dockerfile-debian.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,19 @@ FROM %%%ARCH_PREFIX%%%debian:testing %%%OS%%% +RUN echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" >> /etc/apt/sources.list +RUN echo 'Package: *\n\ +Pin: release a=testing\n\ +Pin-Priority: 900\n\ +\n\ +Package: *\n\ +Pin: release a=unstable\n\ +Pin-Priority: 800\n\ +\n\ +Package: libxmlb-dev\n\ +Pin: release a=unstable\n\ +Pin-Priority: 901\n'\ + > /etc/apt/preferences +RUN cat /etc/apt/preferences RUN echo fubar > /etc/machine-id %%%ARCH_SPECIFIC_COMMAND%%% %%%INSTALL_DEPENDENCIES_COMMAND%%% diff -Nru fwupd-1.0.9/contrib/ci/Dockerfile-fedora.in fwupd-1.2.10/contrib/ci/Dockerfile-fedora.in --- fwupd-1.0.9/contrib/ci/Dockerfile-fedora.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/Dockerfile-fedora.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,10 @@ -FROM fedora:27 +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 -Nru fwupd-1.0.9/contrib/ci/Dockerfile-flatpak.in fwupd-1.2.10/contrib/ci/Dockerfile-flatpak.in --- fwupd-1.0.9/contrib/ci/Dockerfile-flatpak.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/Dockerfile-flatpak.in 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/ci/Dockerfile-snap.in fwupd-1.2.10/contrib/ci/Dockerfile-snap.in --- fwupd-1.0.9/contrib/ci/Dockerfile-snap.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/Dockerfile-snap.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,25 @@ -FROM snapcore/snapcraft -RUN mkdir /build -WORKDIR /build -COPY . . -CMD ["./contrib/ci/snap.sh"] +FROM ubuntu:xenial + +RUN apt-get update && \ + apt-get dist-upgrade --yes && \ + apt-get install --yes \ + curl sudo jq squashfs-tools && \ + curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/core' | jq '.download_url' -r) --output core.snap && \ + mkdir -p /snap/core && unsquashfs -d /snap/core/current core.snap && rm core.snap && \ + curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/snaps/details/snapcraft?channel=edge' | jq '.download_url' -r) --output snapcraft.snap && \ + mkdir -p /snap/snapcraft && unsquashfs -d /snap/snapcraft/current snapcraft.snap && rm snapcraft.snap && \ + apt remove --yes --purge curl jq squashfs-tools && \ + apt-get autoclean --yes && \ + apt-get clean --yes + +COPY contrib/ci/snapcraft-wrapper /snap/bin/snapcraft +ENV PATH=/snap/bin:$PATH + +LABEL maintainer="Mario Limonciello " +RUN apt-get update && apt-get install -y \ + curl \ + git \ + jq \ + openssh-client \ + wget +WORKDIR /root/project diff -Nru fwupd-1.0.9/contrib/ci/Dockerfile-ubuntu.in fwupd-1.2.10/contrib/ci/Dockerfile-ubuntu.in --- fwupd-1.0.9/contrib/ci/Dockerfile-ubuntu.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/Dockerfile-ubuntu.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,6 @@ FROM ubuntu:devel %%%OS%%% -ENV CC clang +ENV CC clang-6.0 RUN echo fubar > /etc/machine-id %%%ARCH_SPECIFIC_COMMAND%%% %%%INSTALL_DEPENDENCIES_COMMAND%%% diff -Nru fwupd-1.0.9/contrib/ci/fedora.sh fwupd-1.2.10/contrib/ci/fedora.sh --- fwupd-1.0.9/contrib/ci/fedora.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/fedora.sh 2019-07-15 18:25:54.000000000 +0000 @@ -7,10 +7,13 @@ mkdir -p build && pushd build rm -rf * meson .. \ + -Db_sanitize=address \ -Dgtkdoc=true \ -Dman=true \ -Dtests=true \ -Dplugin_dummy=true \ + -Dplugin_flashrom=true \ + -Dplugin_modem_manager=false \ -Dplugin_thunderbolt=true \ -Dplugin_uefi=true \ -Dplugin_dell=true \ @@ -46,14 +49,25 @@ #install RPM packages dnf install -y $HOME/rpmbuild/RPMS/*/*.rpm +mkdir -p dist 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 -Nru fwupd-1.0.9/contrib/ci/flatpak.py fwupd-1.2.10/contrib/ci/flatpak.py --- fwupd-1.0.9/contrib/ci/flatpak.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/flatpak.py 2019-07-15 18:25:54.000000000 +0000 @@ -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, strict=False) + 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 -Nru fwupd-1.0.9/contrib/ci/flatpak.sh fwupd-1.2.10/contrib/ci/flatpak.sh --- fwupd-1.0.9/contrib/ci/flatpak.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/flatpak.sh 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/ci/generate_debian_control.py fwupd-1.2.10/contrib/ci/generate_debian_control.py --- fwupd-1.0.9/contrib/ci/generate_debian_control.py 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/generate_debian_control.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright (C) 2017 Dell Inc. -# -# SPDX-License-Identifier: LGPL-2.1+ -# -import os -import sys -import xml.etree.ElementTree as etree - -def parse_control_dependencies(requested_type): - TARGET=os.getenv('OS') - deps = [] - dep = '' - - if TARGET == '': - print("Missing OS environment variable") - sys.exit(1) - OS = TARGET - SUBOS = '' - if TARGET: - split = TARGET.split('-') - if len(split) >= 2: - OS = split[0] - SUBOS = split[1] - else: - import lsb_release - OS = lsb_release.get_distro_information()['ID'].lower() - import platform - SUBOS = platform.machine() - - tree = etree.parse(os.path.join(directory, "dependencies.xml")) - root = tree.getroot() - for child in root: - if not "type" in child.attrib or not "id" in child.attrib: - continue - for distro in child: - if not "id" in distro.attrib: - continue - if distro.attrib["id"] != OS: - continue - control = distro.find("control") - if control is None: - continue - packages = distro.findall("package") - for package in packages: - if SUBOS: - if not 'variant' in package.attrib: - continue - if package.attrib['variant'] != SUBOS: - continue - if package.text: - dep = package.text - else: - dep = child.attrib["id"] - if child.attrib["type"] == requested_type and dep: - version = control.find('version') - if version is not None: - dep = "%s %s" % (dep, version.text) - inclusions = control.findall('inclusive') - if inclusions: - for i in range(0, len(inclusions)): - prefix = '' - suffix = ' ' - if i == 0: - prefix = " [" - if i == len(inclusions) - 1: - suffix = "]" - dep = "%s%s%s%s" % (dep, prefix, inclusions[i].text, suffix) - exclusions = control.findall('exclusive') - if exclusions: - for i in range(0, len(exclusions)): - prefix = '!' - suffix = ' ' - if i == 0: - prefix = " [!" - if i == len(exclusions) - 1: - suffix = "]" - dep = "%s%s%s%s" % (dep, prefix, exclusions[i].text, suffix) - deps.append(dep) - return deps - -directory = os.path.dirname(sys.argv[0]) -if (len(sys.argv) < 3): - print("Missing input and output file") - sys.exit(1) - -deps = parse_control_dependencies("build") - -input = sys.argv[1] -if not os.path.exists(input): - print("Missing input file %s" % input) - sys.exit(1) - -with open(input, 'r') as rfd: - lines = rfd.readlines() - -deps.sort() -output = sys.argv[2] -with open(output, 'w') as wfd: - for line in lines: - if line.startswith("Build-Depends: %%%DYNAMIC%%%"): - wfd.write("Build-Depends:\n") - for i in range(0, len(deps)): - wfd.write("\t%s,\n" % deps[i]) - else: - wfd.write(line) diff -Nru fwupd-1.0.9/contrib/ci/generate_debian.py fwupd-1.2.10/contrib/ci/generate_debian.py --- fwupd-1.0.9/contrib/ci/generate_debian.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/generate_debian.py 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,148 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Dell, Inc. +# +# SPDX-License-Identifier: LGPL-2.1+ +# +import os +import sys +import xml.etree.ElementTree as etree + +def parse_control_dependencies(requested_type): + TARGET=os.getenv('OS') + deps = [] + dep = '' + + if TARGET == '': + print("Missing OS environment variable") + sys.exit(1) + OS = TARGET + SUBOS = '' + if TARGET: + split = TARGET.split('-') + if len(split) >= 2: + OS = split[0] + SUBOS = split[1] + else: + import lsb_release + OS = lsb_release.get_distro_information()['ID'].lower() + import platform + SUBOS = platform.machine() + + tree = etree.parse(os.path.join(os.path.dirname (sys.argv[0]), "dependencies.xml")) + root = tree.getroot() + for child in root: + if not "type" in child.attrib or not "id" in child.attrib: + continue + for distro in child: + if not "id" in distro.attrib: + continue + if distro.attrib["id"] != OS: + continue + control = distro.find("control") + if control is None: + continue + packages = distro.findall("package") + for package in packages: + if SUBOS: + if not 'variant' in package.attrib: + continue + if package.attrib['variant'] != SUBOS: + continue + if package.text: + dep = package.text + else: + dep = child.attrib["id"] + if child.attrib["type"] == requested_type and dep: + version = control.find('version') + if version is not None: + dep = "%s %s" % (dep, version.text) + inclusions = control.findall('inclusive') + if inclusions: + for i in range(0, len(inclusions)): + prefix = '' + suffix = ' ' + if i == 0: + prefix = " [" + if i == len(inclusions) - 1: + suffix = "]" + dep = "%s%s%s%s" % (dep, prefix, inclusions[i].text, suffix) + exclusions = control.findall('exclusive') + if exclusions: + for i in range(0, len(exclusions)): + prefix = '!' + suffix = ' ' + if i == 0: + prefix = " [!" + if i == len(exclusions) - 1: + suffix = "]" + dep = "%s%s%s%s" % (dep, prefix, exclusions[i].text, suffix) + deps.append(dep) + return deps + +def update_debian_control(target): + control_in = os.path.join(target, 'control.in') + control_out = os.path.join(target, 'control') + + if not os.path.exists(control_in): + print("Missing file %s" % control_in) + sys.exit(1) + + with open(control_in, 'r') as rfd: + lines = rfd.readlines() + + deps = parse_control_dependencies("build") + deps.sort() + with open(control_out, 'w') as wfd: + for line in lines: + if line.startswith("Build-Depends: %%%DYNAMIC%%%"): + wfd.write("Build-Depends:\n") + for i in range(0, len(deps)): + wfd.write("\t%s,\n" % deps[i]) + else: + wfd.write(line) + +def update_debian_copyright (directory): + copyright_in = os.path.join(directory, 'copyright.in') + copyright_out = os.path.join(directory, 'copyright') + + if not os.path.exists(copyright_in): + print("Missing file %s" % copyright_in) + sys.exit(1) + + # Assume all files are remaining LGPL-2.1+ + copyrights = [] + for root, dirs, files in os.walk('.'): + for file in files: + target = os.path.join (root, file) + #skip translations and license file + if target.startswith('./po/') or file == "COPYING": + continue + try: + with open(target, 'r') as rfd: + #read about the first few lines of the file only + lines = rfd.readlines(220) + except UnicodeDecodeError: + continue + for line in lines: + if 'Copyright (C) ' in line: + parts = line.split ('Copyright (C)')[1].strip() #split out the copyright header + partition = parts.partition(' ')[2] # remove the year string + copyrights += ["%s" % partition] + copyrights = "\n\t ".join(sorted(set(copyrights))) + with open(copyright_in, 'r') as rfd: + lines = rfd.readlines() + + with open(copyright_out, 'w') as wfd: + for line in lines: + if line.startswith("%%%DYNAMIC%%%"): + wfd.write("Files: *\n") + wfd.write("Copyright: %s\n" % copyrights) + wfd.write("License: LGPL-2.1+\n") + wfd.write("\n") + else: + wfd.write(line) + +directory = os.path.join (os.getcwd(), 'debian') +update_debian_control(directory) +update_debian_copyright(directory) diff -Nru fwupd-1.0.9/contrib/ci/generate_docker.py fwupd-1.2.10/contrib/ci/generate_docker.py --- fwupd-1.0.9/contrib/ci/generate_docker.py 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/generate_docker.py 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -# Copyright (C) 2017-2018 Dell Inc. +# Copyright (C) 2017-2018 Dell, Inc. # # SPDX-License-Identifier: LGPL-2.1+ # @@ -65,7 +65,7 @@ with open(out.name, 'w') as wfd: for line in lines: if line.startswith("FROM %%%ARCH_PREFIX%%%"): - if OS == "debian" and SUBOS == "i386": + if (OS == "debian" or OS == "ubuntu") and SUBOS == "i386": replace = SUBOS + "/" else: replace = '' diff -Nru fwupd-1.0.9/contrib/ci/README.md fwupd-1.2.10/contrib/ci/README.md --- fwupd-1.0.9/contrib/ci/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -9,11 +9,12 @@ ------ * A fully packaged RPM build with all plugins enabled -* Compiled under gcc +* Compiled under gcc with AddressSanitizer * Tests with -Werror enabled * Tests with the built in local test suite for all plugins. * All packages are installed * An installed testing run with the "test" plugin and pulling from LVFS. +* With modem manager disabled Debian testing (x86_64) ------ @@ -52,9 +53,12 @@ ------ * Not packaged +* Tests for missing translation files +* No redfish support * Compiled under gcc * Tests with -Werror enabled * Runs local test suite using qemu-user +* Modem manager disabled Arch Linux (x86_64) ---------- @@ -86,7 +90,7 @@ The child elements represent individual dependencies for all distributions. * This element has an attribute ***id*** that represents the most common package name used by distributions -* This element has an attribute ***type*** that reprsents if the package is needed at build time or runtime. +* This element has an attribute ***type*** that represents if the package is needed at build time or runtime. Each dependency then has a mapping to individual distributions (___distro___). * This element has an attribute ***id*** that represents the distribution. diff -Nru fwupd-1.0.9/contrib/ci/snapcraft-wrapper fwupd-1.2.10/contrib/ci/snapcraft-wrapper --- fwupd-1.0.9/contrib/ci/snapcraft-wrapper 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/snapcraft-wrapper 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,12 @@ +#!/bin/sh +SNAP="/snap/snapcraft/current" +SNAP_NAME="$(awk '/^name:/{print $2}' $SNAP/meta/snap.yaml)" +SNAP_VERSION="$(awk '/^version:/{print $2}' $SNAP/meta/snap.yaml)" +SNAP_ARCH="amd64" + +export SNAP +export SNAP_NAME +export SNAP_VERSION +export SNAP_ARCH + +exec "$SNAP/usr/bin/python3" "$SNAP/bin/snapcraft" "$@" diff -Nru fwupd-1.0.9/contrib/ci/ubuntu.sh fwupd-1.2.10/contrib/ci/ubuntu.sh --- fwupd-1.0.9/contrib/ci/ubuntu.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/ci/ubuntu.sh 2019-07-15 18:25:54.000000000 +0000 @@ -1,69 +1,15 @@ -#!/bin/bash +#!/bin/sh set -e set -x -#although it's debian, we don't build packages -if [ "$OS" = "debian-s390x" ]; then - ./contrib/ci/debian_s390x.sh - exit 0 -fi - -#prepare -export DEBFULLNAME="CI Builder" -export DEBEMAIL="ci@travis-ci.org" -VERSION=`git describe | sed 's/-/+r/;s/-/+/'` -[ -z $VERSION ] && VERSION=`head meson.build | grep ' version :' | cut -d \' -f2` -rm -rf build/ -mkdir -p build -shopt -s extglob -cp -lR !(build|dist) build/ -pushd build -mv contrib/debian . -sed s/quilt/native/ debian/source/format -i -#generate control file -./contrib/ci/generate_debian_control.py debian/control.in debian/control -#build the package -EDITOR=/bin/true dch --create --package fwupd -v $VERSION "CI Build" -debuild --no-lintian --preserve-envvar CI --preserve-envvar CC - -#check lintian output -#suppress tags that are side effects of building in docker this way -lintian ../*changes \ - -IE \ - --pedantic \ - --no-tag-display-limit \ - --suppress-tags bad-distribution-in-changes-file \ - --suppress-tags source-contains-unsafe-symlink \ - --suppress-tags changelog-should-mention-nmu \ - --suppress-tags debian-watch-file-in-native-package \ - --suppress-tags source-nmu-has-incorrect-version-number \ - --suppress-tags no-symbols-control-file \ - --allow-root - -#if invoked outside of CI -if [ ! -f /.dockerenv ]; then - echo "Not running in a container, please manually install packages" - exit 0 -fi - -#test the packages install -PACKAGES=$(ls ../*.deb | grep -v 'fwupd-tests\|dbgsym') -dpkg -i $PACKAGES - -# run the installed tests -if [ "$CI" = "true" ]; then - dpkg -i ../fwupd-tests*.deb - service dbus restart - gnome-desktop-testing-runner fwupd - apt purge -y fwupd-tests -fi - -#test the packages remove -apt purge -y fwupd \ - fwupd-doc \ - libfwupd2 \ - libfwupd-dev - -#place built packages in dist outside docker -mkdir -p ../dist -cp $PACKAGES ../dist +#evaluate using Ubuntu's buildflags +eval "$(dpkg-buildflags --export=sh)" +#filter out -Bsymbolic-functions +export LDFLAGS=$(dpkg-buildflags --get LDFLAGS | sed "s/-Wl,-Bsymbolic-functions\s//") + +rm -rf build +meson build --werror +#build with clang and -Werror +ninja -C build test -v +#run static analysis (these mostly won't be critical) +ninja -C build scan-build -v diff -Nru fwupd-1.0.9/contrib/debian/compat fwupd-1.2.10/contrib/debian/compat --- fwupd-1.0.9/contrib/debian/compat 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/compat 2019-07-15 18:25:54.000000000 +0000 @@ -1 +1 @@ -10 +11 diff -Nru fwupd-1.0.9/contrib/debian/control.in fwupd-1.2.10/contrib/debian/control.in --- fwupd-1.0.9/contrib/debian/control.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/control.in 2019-07-15 18:25:54.000000000 +0000 @@ -6,7 +6,7 @@ Matthias Klumpp , Mario Limonciello Build-Depends: %%%DYNAMIC%%% -Standards-Version: 4.1.4 +Standards-Version: 4.3.0 Section: admin Homepage: https://github.com/hughsie/fwupd Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git @@ -22,8 +22,7 @@ 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. @@ -31,9 +30,13 @@ Package: fwupd Architecture: linux-any Depends: ${misc:Depends}, - ${shlibs:Depends} -Recommends: fwupdate, - python3 + ${shlibs:Depends}, + shared-mime-info +Recommends: python3, + bolt, + tpm2-tools, + tpm2-abrmd, + fwupd-signed Breaks: gir1.2-dfu-1.0 (<< 0.9.7-1), libdfu1 (<< 0.9.7-1), libdfu-dev (<< 0.9.7-1) @@ -45,8 +48,7 @@ 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 @@ -68,8 +70,7 @@ 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 @@ -84,8 +85,7 @@ 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 @@ -104,8 +104,7 @@ 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 @@ -121,3 +120,31 @@ . It can be used by packages using the GIRepository format to generate dynamic bindings. + +Package: fwupd-amd64-signed-template +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-i386-signed-template +Architecture: i386 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-armhf-signed-template +Architecture: armhf +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-arm64-signed-template +Architecture: arm64 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. diff -Nru fwupd-1.0.9/contrib/debian/copyright fwupd-1.2.10/contrib/debian/copyright --- fwupd-1.0.9/contrib/debian/copyright 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/copyright 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: fwupd -Source: https://github.com/hughsie/fwupd - -Files: * -Copyright: 2015 Richard Hughes -License: LGPL-2.1+ - -Files: data/tests/colorhug/firmware.metainfo.xml -Copyright: 2015 Richard Hughes -License: CC0-1.0 - -Files: debian/* -Copyright: 2015 Daniel Jared Dominguez - 2015 Mario Limonciello -License: LGPL-2.1+ - -License: LGPL-2.1+ - This package is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU Lesser General - Public License version 2.1 can be found in "/usr/share/common-licenses/LGPL-2.1". - - -License: CC0-1.0 - Creative Commons CC0 1.0 Universal - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL - SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT - RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. - CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE - INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES - RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - . - Statement of Purpose - . - The laws of most jurisdictions throughout the world automatically confer - exclusive Copyright and Related Rights (defined below) upon the creator and - subsequent owner(s) (each and all, an "owner") of an original work of - authorship and/or a database (each, a "Work"). Certain owners wish to - permanently relinquish those rights to a Work for the purpose of contributing - to a commons of creative, cultural and scientific works ("Commons") that the - public can reliably and without fear of later claims of infringement build - upon, modify, incorporate in other works, reuse and redistribute as freely as - possible in any form whatsoever and for any purposes, including without - limitation commercial purposes. These owners may contribute to the Commons to - promote the ideal of a free culture and the further production of creative, - cultural and scientific works, or to gain reputation or greater distribution - for their Work in part through the use and efforts of others. For these and/or - other purposes and motivations, and without any expectation of additional - consideration or compensation, the person associating CC0 with a Work (the - "Affirmer"), to the extent that he or she is an owner of Copyright and Related - Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly - distribute the Work under its terms, with knowledge of his or her Copyright and - Related Rights in the Work and the meaning and intended legal effect of CC0 on - those rights. - . - 1. Copyright and Related Rights. A Work made available under CC0 may be - protected by copyright and related or neighboring rights ("Copyright and - Related Rights"). Copyright and Related Rights include, but are not limited to, - the following: - i. the right to reproduce, adapt, distribute, perform, display, communicate, - and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); - iii. publicity and privacy rights pertaining to a person's image or likeness - depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, subject - to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data in a - Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal protection - of databases, and under any national implementation thereof, including any - amended or successor version of such directive); and - vii. other similar, equivalent or corresponding rights throughout the world - based on applicable law or treaty, and any national implementations thereof. - . - 2. Waiver. To the greatest extent permitted by, but not in contravention of, - applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and - unconditionally waives, abandons, and surrenders all of Affirmer's Copyright - and Related Rights and associated claims and causes of action, whether now - known or unknown (including existing as well as future claims and causes of - action), in the Work (i) in all territories worldwide, (ii) for the maximum - duration provided by applicable law or treaty (including future time - extensions), (iii) in any current or future medium and for any number of - copies, and (iv) for any purpose whatsoever, including without limitation - commercial, advertising or promotional purposes (the "Waiver"). - Affirmer makes the Waiver for the benefit of each member of the public at large - and to the detriment of Affirmer's heirs and successors, fully intending that - such Waiver shall not be subject to revocation, rescission, cancellation, - termination, or any other legal or equitable action to disrupt the quiet - enjoyment of the Work by the public as contemplated by Affirmer's express - Statement of Purpose. - . - 3. Public License Fallback. Should any part of the Waiver for any reason be - judged legally invalid or ineffective under applicable law, then the Waiver - shall be preserved to the maximum extent permitted taking into account - Affirmer's express Statement of Purpose. In addition, to the extent the Waiver - is so judged Affirmer hereby grants to each affected person a royalty-free, non - transferable, non sublicensable, non exclusive, irrevocable and unconditional - license to exercise Affirmer's Copyright and Related Rights in the Work (i) in - all territories worldwide, (ii) for the maximum duration provided by - applicable law or treaty (including future time extensions), (iii) in any - current or future medium and for any number of copies, and (iv) for any - purpose whatsoever, including without limitation commercial, advertising or - promotional purposes (the "License"). The License shall be deemed effective - as of the date CC0 was applied by Affirmer to the Work. Should any part of - the License for any reason be judged legally invalid or ineffective under - applicable law, such partial invalidity or ineffectiveness shall not - invalidate the remainder of the License, and in such case Affirmer hereby - affirms that he or she will not (i) exercise any of his or her remaining - Copyright and Related Rights in the Work or (ii) assert any associated claims - and causes of action with respect to the Work, in either case contrary to - Affirmer's express Statement of Purpose. - . - 4. Limitations and Disclaimers. - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or warranties of - any kind concerning the Work, express, implied, statutory or otherwise, - including without limitation warranties of title, merchantability, fitness for - a particular purpose, non infringement, or the absence of latent or other - defects, accuracy, or the present or absence of errors, whether or not - discoverable, all to the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons that - may apply to the Work or any use thereof, including without limitation any - person's Copyright and Related Rights in the Work. Further, Affirmer disclaims - responsibility for obtaining any necessary consents, permissions or other - rights required for any use of the Work. - d. Affirmer understands and acknowledges that Creative Commons is not a party - to this document and has no duty or obligation with respect to this CC0 or use - of the Work. diff -Nru fwupd-1.0.9/contrib/debian/copyright.in fwupd-1.2.10/contrib/debian/copyright.in --- fwupd-1.0.9/contrib/debian/copyright.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/copyright.in 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,140 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: fwupd +Source: https://github.com/hughsie/fwupd + +%%%DYNAMIC%%% +Files: *.metainfo.xml +Copyright: Richard Hughes +License: CC0-1.0 + +Files: debian/* +Copyright: 2015 Daniel Jared Dominguez + 2015-2018 Mario Limonciello +License: LGPL-2.1+ + +License: LGPL-2.1+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 2.1 can be found in "/usr/share/common-licenses/LGPL-2.1". + + +License: CC0-1.0 + Creative Commons CC0 1.0 Universal + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL + SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT + RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. + CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE + INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES + RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + . + Statement of Purpose + . + The laws of most jurisdictions throughout the world automatically confer + exclusive Copyright and Related Rights (defined below) upon the creator and + subsequent owner(s) (each and all, an "owner") of an original work of + authorship and/or a database (each, a "Work"). Certain owners wish to + permanently relinquish those rights to a Work for the purpose of contributing + to a commons of creative, cultural and scientific works ("Commons") that the + public can reliably and without fear of later claims of infringement build + upon, modify, incorporate in other works, reuse and redistribute as freely as + possible in any form whatsoever and for any purposes, including without + limitation commercial purposes. These owners may contribute to the Commons to + promote the ideal of a free culture and the further production of creative, + cultural and scientific works, or to gain reputation or greater distribution + for their Work in part through the use and efforts of others. For these and/or + other purposes and motivations, and without any expectation of additional + consideration or compensation, the person associating CC0 with a Work (the + "Affirmer"), to the extent that he or she is an owner of Copyright and Related + Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly + distribute the Work under its terms, with knowledge of his or her Copyright and + Related Rights in the Work and the meaning and intended legal effect of CC0 on + those rights. + . + 1. Copyright and Related Rights. A Work made available under CC0 may be + protected by copyright and related or neighboring rights ("Copyright and + Related Rights"). Copyright and Related Rights include, but are not limited to, + the following: + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, subject + to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data in a + Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal protection + of databases, and under any national implementation thereof, including any + amended or successor version of such directive); and + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + . + 2. Waiver. To the greatest extent permitted by, but not in contravention of, + applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and + unconditionally waives, abandons, and surrenders all of Affirmer's Copyright + and Related Rights and associated claims and causes of action, whether now + known or unknown (including existing as well as future claims and causes of + action), in the Work (i) in all territories worldwide, (ii) for the maximum + duration provided by applicable law or treaty (including future time + extensions), (iii) in any current or future medium and for any number of + copies, and (iv) for any purpose whatsoever, including without limitation + commercial, advertising or promotional purposes (the "Waiver"). + Affirmer makes the Waiver for the benefit of each member of the public at large + and to the detriment of Affirmer's heirs and successors, fully intending that + such Waiver shall not be subject to revocation, rescission, cancellation, + termination, or any other legal or equitable action to disrupt the quiet + enjoyment of the Work by the public as contemplated by Affirmer's express + Statement of Purpose. + . + 3. Public License Fallback. Should any part of the Waiver for any reason be + judged legally invalid or ineffective under applicable law, then the Waiver + shall be preserved to the maximum extent permitted taking into account + Affirmer's express Statement of Purpose. In addition, to the extent the Waiver + is so judged Affirmer hereby grants to each affected person a royalty-free, non + transferable, non sublicensable, non exclusive, irrevocable and unconditional + license to exercise Affirmer's Copyright and Related Rights in the Work (i) in + all territories worldwide, (ii) for the maximum duration provided by + applicable law or treaty (including future time extensions), (iii) in any + current or future medium and for any number of copies, and (iv) for any + purpose whatsoever, including without limitation commercial, advertising or + promotional purposes (the "License"). The License shall be deemed effective + as of the date CC0 was applied by Affirmer to the Work. Should any part of + the License for any reason be judged legally invalid or ineffective under + applicable law, such partial invalidity or ineffectiveness shall not + invalidate the remainder of the License, and in such case Affirmer hereby + affirms that he or she will not (i) exercise any of his or her remaining + Copyright and Related Rights in the Work or (ii) assert any associated claims + and causes of action with respect to the Work, in either case contrary to + Affirmer's express Statement of Purpose. + . + 4. Limitations and Disclaimers. + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or warranties of + any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness for + a particular purpose, non infringement, or the absence of latent or other + defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons that + may apply to the Work or any use thereof, including without limitation any + person's Copyright and Related Rights in the Work. Further, Affirmer disclaims + responsibility for obtaining any necessary consents, permissions or other + rights required for any use of the Work. + d. Affirmer understands and acknowledges that Creative Commons is not a party + to this document and has no duty or obligation with respect to this CC0 or use + of the Work. diff -Nru fwupd-1.0.9/contrib/debian/docs fwupd-1.2.10/contrib/debian/docs --- fwupd-1.0.9/contrib/debian/docs 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/docs 2019-07-15 18:25:54.000000000 +0000 @@ -1 +1 @@ -NEWS + diff -Nru fwupd-1.0.9/contrib/debian/fwupd.install fwupd-1.2.10/contrib/debian/fwupd.install --- fwupd-1.0.9/contrib/debian/fwupd.install 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/fwupd.install 2019-07-15 18:25:54.000000000 +0000 @@ -4,13 +4,17 @@ usr/share/bash-completion usr/share/fwupd/* usr/share/dbus-1/* +usr/share/icons/* usr/share/polkit-1/* usr/share/locale usr/share/metainfo/* usr/lib/*/fwupd +usr/lib/*/fwupdagent +usr/lib/*/fwupdoffline usr/lib/*/fwupdtool usr/share/man/man1/* lib/systemd/system/* +lib/systemd/system-shutdown/* var/lib/fwupd lib/udev/rules.d/* data/daemon.conf etc/fwupd diff -Nru fwupd-1.0.9/contrib/debian/fwupd.postinst fwupd-1.2.10/contrib/debian/fwupd.postinst --- fwupd-1.0.9/contrib/debian/fwupd.postinst 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/fwupd.postinst 2019-07-15 18:25:54.000000000 +0000 @@ -6,4 +6,6 @@ if dpkg-maintscript-helper supports rm_conffile 2>/dev/null; then dpkg-maintscript-helper rm_conffile \ /etc/fwupd.conf 1.0.0~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/fwupd/remotes.d/fwupd.conf 1.2.7~ -- "$@" fi diff -Nru fwupd-1.0.9/contrib/debian/fwupd-tests.install fwupd-1.2.10/contrib/debian/fwupd-tests.install --- fwupd-1.0.9/contrib/debian/fwupd-tests.install 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/fwupd-tests.install 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/debian/fwupd-tests.postinst fwupd-1.2.10/contrib/debian/fwupd-tests.postinst --- fwupd-1.0.9/contrib/debian/fwupd-tests.postinst 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/fwupd-tests.postinst 2019-07-15 18:25:54.000000000 +0000 @@ -12,4 +12,12 @@ 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 -Nru fwupd-1.0.9/contrib/debian/gen_signing_changelog fwupd-1.2.10/contrib/debian/gen_signing_changelog --- fwupd-1.0.9/contrib/debian/gen_signing_changelog 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/gen_signing_changelog 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Generate a changelog file for the signed fwupdate package, based on +# a changelog.in file and other state + +DIR=$1 +SOURCE=$2 +ARCH=$3 +IN="${DIR}/changelog.in" +OUT="${DIR}/changelog" + +# Parse out fields from our changelg entry - want the signing-template +# one to match all the important details where we can +DISTRIBUTION="$(dpkg-parsechangelog | sed -ne 's/^Distribution: \(.*\)/\1/p')" +URGENCY="$(dpkg-parsechangelog | sed -ne 's/^Urgency: \(.*\)/\1/p')" +MAINT="$(dpkg-parsechangelog | sed -ne 's/^Maintainer: \(.*\)/\1/p')" +DATE="$(dpkg-parsechangelog | sed -ne 's/^Date: \(.*\)/\1/p')" + +# If the version ends in "+bXXX", this is a binNMU. We don't want a new +# source package to look like that, so change it to ".bXXX" instead +VERSION="$(dpkg-parsechangelog | sed -ne 's/^Version: \(.*\)/\1/p')" +MANGLED_VERSION="$(echo $VERSION | sed -r 's/-/\+/;s/\+(b[[:digit:]]+)$/.\1/')" + +printf "%s-%s-signed (%s) %s; urgency=%s\n" "${SOURCE}" "${ARCH}" "${MANGLED_VERSION}" "${DISTRIBUTION}" "${URGENCY}" > $OUT +printf "\n" >> $OUT +printf " * Update to %s version %s\n" "${SOURCE}" "${VERSION}" >> $OUT +printf "\n" >> $OUT +printf " -- %s %s\n" "${MAINT}" "${DATE}" >> $OUT +printf "\n" >> $OUT + +cat $IN >> $OUT +rm -f $IN diff -Nru fwupd-1.0.9/contrib/debian/gen_signing_json fwupd-1.2.10/contrib/debian/gen_signing_json --- fwupd-1.0.9/contrib/debian/gen_signing_json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/gen_signing_json 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Generate a json file to go in the the fwupd-signed template +# package. Describes exactly what needs to be signed, and how. + +DIR=$1 +SOURCE=$2 +ARCH=$3 +OUT="$DIR/files.json" + +# What file are we looking to sign? +BINARY=$(find debian/tmp -name '*.efi' | xargs basename) + +# Actually needs full path within the binary deb +BINARY="usr/lib/${SOURCE}/efi/${BINARY}" + +rm -f $OUT + +printf '{\n' >> $OUT +printf ' "packages": {\n' >> $OUT +printf ' "%s": {\n' "${SOURCE}" >> $OUT +printf ' "trusted_certs": [],\n' >> $OUT +printf ' "files": [ \n' >> $OUT +printf ' {"sig_type": "efi", "file": "%s"}\n' "${BINARY}" >> $OUT +printf ' ]\n' >> $OUT +printf ' }\n' >> $OUT +printf ' }\n' >> $OUT +printf '}\n' >> $OUT + diff -Nru fwupd-1.0.9/contrib/debian/gir1.2-fwupd-2.0.install fwupd-1.2.10/contrib/debian/gir1.2-fwupd-2.0.install --- fwupd-1.0.9/contrib/debian/gir1.2-fwupd-2.0.install 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/gir1.2-fwupd-2.0.install 2019-07-15 18:25:54.000000000 +0000 @@ -1 +1 @@ -usr/lib/*/girepository-1.0/Fwupd-2.0.typelib +usr/lib/*/girepository-1.0/*.typelib diff -Nru fwupd-1.0.9/contrib/debian/libfwupd2.install fwupd-1.2.10/contrib/debian/libfwupd2.install --- fwupd-1.0.9/contrib/debian/libfwupd2.install 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/libfwupd2.install 2019-07-15 18:25:54.000000000 +0000 @@ -1 +1 @@ -usr/lib/*/libfwup*.so.* +usr/lib/*/*.so.* diff -Nru fwupd-1.0.9/contrib/debian/libfwupd-dev.install fwupd-1.2.10/contrib/debian/libfwupd-dev.install --- fwupd-1.0.9/contrib/debian/libfwupd-dev.install 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/libfwupd-dev.install 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/debian/lintian/fwupd fwupd-1.2.10/contrib/debian/lintian/fwupd --- fwupd-1.0.9/contrib/debian/lintian/fwupd 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/lintian/fwupd 2019-07-15 18:25:54.000000000 +0000 @@ -4,3 +4,7 @@ fwupd binary: systemd-service-file-missing-install-key lib/systemd/system/system-update.target.wants/fwupd-offline-update.service #see debian bug 896012 fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_upower.so +#EFI applications are PE executables +fwupd: executable-not-elf-or-script usr/lib/fwupd/efi/*.efi +fwupd: portable-executable-missing-security-features usr/lib/fwupd/efi/*.efi ASLR DEP/NX +fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_modem_manager.so diff -Nru fwupd-1.0.9/contrib/debian/README.Debian fwupd-1.2.10/contrib/debian/README.Debian --- fwupd-1.0.9/contrib/debian/README.Debian 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/README.Debian 2019-07-15 18:25:54.000000000 +0000 @@ -1,7 +1,18 @@ -fwupd for Debian ----------------- +signed vs unsigned fwupd programs +------------------------------------ -fwupd is still heavily in development. As of this date, the functionality -it provides is not yet available on most systems. +fwupd 1.1.0 is configured to understand when to use a signed version +of the EFI binary. If the signed version isn't installed but secure +boot is turned on, it will avoid copying to the EFI system partition. + +This allows supporting secure boot even if not turned on at install, or +changed later after install. + +In Ubuntu, both fwupd-signed and fwupd are seeded in the default +installation. Nothing is installed to the ESP until it's needed. + +In Debian, the package name for the signed version is slightly +different due to different infrastructure. fwupd-signed-$ARCH and +fwupd should both be installed and then things will work similarly +to what's described above. - -- Daniel Jared Dominguez Wed, 20 May 2015 17:16:02 -0500 diff -Nru fwupd-1.0.9/contrib/debian/rules fwupd-1.2.10/contrib/debian/rules --- fwupd-1.0.9/contrib/debian/rules 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/rules 2019-07-15 18:25:54.000000000 +0000 @@ -3,6 +3,7 @@ export LC_ALL := C.UTF-8 export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_STRIP=-Wl,-Bsymbolic-functions #GPGME needs this for proper building on 32 bit archs ifeq "$(DEB_HOST_ARCH_BITS)" "32" @@ -10,36 +11,80 @@ endif ifneq ($(CI),) - export CI=--werror + export CI=--werror --wrap-mode=default +endif + +SB_STYLE := debian +deb_version := $(shell dpkg-parsechangelog --show-field Version) +ifeq (yes,$(shell dpkg-vendor --derives-from Ubuntu && echo yes)) + SB_STYLE := ubuntu + tar_name := fwupd_$(deb_version)_$(DEB_HOST_ARCH).tar.gz +else + TMPLDIR := debian/fwupd-$(DEB_HOST_ARCH)-signed-template/usr/share/code-signing/fwupd-$(DEB_HOST_ARCH)-signed-template endif %: - dh $@ --with gir,systemd + dh $@ --with gir override_dh_auto_clean: rm -fr debian/build +ifeq (ubuntu,$(SB_STYLE)) + rm -rf debian/fwupd-images +endif override_dh_auto_configure: - if pkg-config --exists fwup; then \ - export UEFI="-Dplugin_uefi=true"; \ + if pkg-config --exists libsmbios_c; then \ + export DELL="-Dplugin_dell=true"; \ else \ - export UEFI="-Dplugin_uefi=false"; \ + export DELL="-Dplugin_dell=false"; \ fi; \ - if pkg-config --exists libsmbios_c; then \ - export DELL="-Dplugin_dell=true -Dplugin_synaptics=true"; \ + if pkg-config --exists efivar; then \ + export UEFI="-Dplugin_uefi=true -Dplugin_redfish=true -Dplugin_nvme=true"; \ + else \ + export UEFI="-Dplugin_uefi=false -Dplugin_redfish=false -Dplugin_nvme=false"; \ + fi; \ + if [ ! -z "$$CI" ]; then \ + export FLASHROM="-Dplugin_flashrom=true"; \ else \ - export DELL="-Dplugin_dell=false -Dplugin_synaptics=false"; \ + export FLASHROM="-Dplugin_flashrom=false"; \ fi; \ - dh_auto_configure -- $$UEFI $$DELL $$CI -Dplugin_dummy=true --libexecdir=/usr/lib + dh_auto_configure -- $$UEFI $$DELL $$FLASHROM $$CI -Dplugin_dummy=true --libexecdir=/usr/lib override_dh_install: find debian/tmp/usr -type f -name "*a" -print | xargs rm -f sed -i 's,wheel,sudo,' ./debian/tmp/usr/share/polkit-1/rules.d/org.freedesktop.fwupd.rules dh_install - dh_missing --fail-missing + #install the EFI binaries if needed + if [ -d debian/tmp/usr/lib/fwupd/efi/ ]; then \ + 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 [ ! -z "$$CI" ] && [ -f debian/tmp/usr/lib/xb-tool ]; then \ + dh_install -pfwupd usr/lib/xb-tool ;\ + fi + if [ ! -z "$$CI" ] && [ -f debian/tmp/usr/sbin/flashrom ]; then \ + dh_install -pfwupd usr/sbin/flashrom ;\ + 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 + mkdir -p $(TMPLDIR)/source-template/debian + cp -a debian/signing-template/* $(TMPLDIR)/source-template/debian + cp debian/README.Debian $(TMPLDIR)/source-template/debian + find $(TMPLDIR)/source-template/debian -type f | xargs sed -i "s,SIGNARCH,$(DEB_HOST_ARCH)," + find $(TMPLDIR)/source-template/debian -type f | xargs sed -i "s,SIGNVERSION,$(deb_version)," + for file in $$(find $(TMPLDIR)/source-template/debian -type f -name *SIGNARCH*); do file1=$$(echo $$file | sed "s,SIGNARCH,$(DEB_HOST_ARCH),"); mv -v $$file $$file1; done + install -m 0755 debian/fwupd.postinst $(TMPLDIR)/source-template/debian/fwupd-$(DEB_HOST_ARCH)-signed.postinst + install -m 0755 debian/fwupd.postrm $(TMPLDIR)/source-template/debian/fwupd-$(DEB_HOST_ARCH)-signed.postrm + ./debian/gen_signing_changelog $(TMPLDIR)/source-template/debian fwupd $(DEB_HOST_ARCH) + ./debian/gen_signing_json $(TMPLDIR) fwupd ${DEB_HOST_ARCH} +endif override_dh_strip_nondeterminism: dh_strip_nondeterminism -Xfirmware-example.xml.gz @@ -48,3 +93,18 @@ if [ -x /usr/bin/valgrind ] ; then \ dh_auto_test; \ fi + +override_dh_builddeb: + dh_builddeb +ifeq (ubuntu,$(SB_STYLE)) + if [ -d debian/tmp/usr/lib/fwupd/efi/ ]; then \ + mkdir -p debian/fwupd-images/$(deb_version) ;\ + cp debian/tmp/usr/lib/fwupd/efi/fwupd*.efi debian/fwupd-images/$(deb_version) ;\ + echo $(deb_version) > debian/fwupd-images/$(deb_version)/version ;\ + tar -C debian/fwupd-images -czvf ../$(tar_name) . ;\ + dpkg-distaddfile $(tar_name) raw-uefi - ;\ + fi +endif + +override_dh_shlibdeps: + dh_shlibdeps $$DHSLIBS diff -Nru fwupd-1.0.9/contrib/debian/signing-template/changelog.in fwupd-1.2.10/contrib/debian/signing-template/changelog.in --- fwupd-1.0.9/contrib/debian/signing-template/changelog.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/changelog.in 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,5 @@ +fwupd-SIGNARCH-signed (1) unstable; urgency=medium + + * Add template source package for signing + + -- Steve McIntyre <93sam@debian.org> Sat, 07 Apr 2018 12:44:55 +0100 diff -Nru fwupd-1.0.9/contrib/debian/signing-template/compat fwupd-1.2.10/contrib/debian/signing-template/compat --- fwupd-1.0.9/contrib/debian/signing-template/compat 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/compat 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +9 diff -Nru fwupd-1.0.9/contrib/debian/signing-template/control fwupd-1.2.10/contrib/debian/signing-template/control --- fwupd-1.0.9/contrib/debian/signing-template/control 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/control 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,24 @@ +Source: fwupd-SIGNARCH-signed +Priority: optional +Maintainer: Debian EFI +Uploaders: Daniel Jared Dominguez , Steve McIntyre <93sam@debian.org>, Mario Limonciello +Build-Depends: debhelper (>= 9.0.0), sbsigntool [amd64 arm64 armhf i386], fwupd (= SIGNVERSION) [SIGNARCH] +Standards-Version: 4.1.3 +Section: libs +Homepage: https://github.com/hughsie/fwupd +Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git +Vcs-Browser: https://salsa.debian.org/efi-team/fwupd + +Package: fwupd-SIGNARCH-signed +Section: admin +Architecture: SIGNARCH +Provides: fwupd-signed +Depends: ${shlibs:Depends}, ${misc:Depends}, fwupd (= SIGNVERSION) +Description: Tools to manage UEFI firmware updates (signed) + fwupd provides functionality to update system firmware. It has been + initially designed to update firmware using UEFI capsule updates, but + it is designed to be extensible to other firmware update standards. + . + This package contains just the signed version of the fwupd binary, + needed if your system has UEFI Secure Boot enabled. It depends on the + normal fwupd package for everything else. diff -Nru fwupd-1.0.9/contrib/debian/signing-template/copyright fwupd-1.2.10/contrib/debian/signing-template/copyright --- fwupd-1.0.9/contrib/debian/signing-template/copyright 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/copyright 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,33 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: fwupd +Source: https://github.com/hughsie/fwupd + +Files: * +Copyright: 2015 Richard Hughes +License: LGPL-2.1+ + +Files: data/tests/colorhug/firmware.metainfo.xml +Copyright: 2015 Richard Hughes +License: CC0-1.0 + +Files: debian/* +Copyright: 2015 Daniel Jared Dominguez + 2015 Mario Limonciello +License: LGPL-2.1+ + +License: LGPL-2.1+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 2.1 can be found in "/usr/share/common-licenses/LGPL-2.1". diff -Nru fwupd-1.0.9/contrib/debian/signing-template/fwupd-SIGNARCH-signed.install fwupd-1.2.10/contrib/debian/signing-template/fwupd-SIGNARCH-signed.install --- fwupd-1.0.9/contrib/debian/signing-template/fwupd-SIGNARCH-signed.install 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/fwupd-SIGNARCH-signed.install 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +*.efi.signed /usr/lib/fwupd/efi diff -Nru fwupd-1.0.9/contrib/debian/signing-template/README.source fwupd-1.2.10/contrib/debian/signing-template/README.source --- fwupd-1.0.9/contrib/debian/signing-template/README.source 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/README.source 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,4 @@ +This source package is generated by the Debian signing service from a +template built by the fwupd package. It should never be updated directly. + + -- Steve McIntyre <93sam@debian.org> Sat, 07 Apr 2018 12:44:55 +0100 diff -Nru fwupd-1.0.9/contrib/debian/signing-template/rules fwupd-1.2.10/contrib/debian/signing-template/rules --- fwupd-1.0.9/contrib/debian/signing-template/rules 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/rules 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,15 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +PACKAGE_NAME := fwupd +SIG_PKG_NAME := fwupd-SIGNARCH-signed +SIGNATURE_DIR := debian/signatures/$(PACKAGE_NAME) +BINARY := $(shell find /usr/lib/fwupd/efi -name '*.efi' | xargs basename) + +%: + dh $@ + +override_dh_auto_build: + cp /usr/lib/fwupd/efi/$(BINARY) . + sbattach --attach $(SIGNATURE_DIR)/usr/lib/fwupd/efi/$(BINARY).sig $(BINARY) + mv $(BINARY) $(BINARY).signed diff -Nru fwupd-1.0.9/contrib/debian/signing-template/source/format fwupd-1.2.10/contrib/debian/signing-template/source/format --- fwupd-1.0.9/contrib/debian/signing-template/source/format 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/signing-template/source/format 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +3.0 (native) diff -Nru fwupd-1.0.9/contrib/debian/source/lintian-overrides fwupd-1.2.10/contrib/debian/source/lintian-overrides --- fwupd-1.0.9/contrib/debian/source/lintian-overrides 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/debian/source/lintian-overrides 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/firmware-packager/firmware-packager fwupd-1.2.10/contrib/firmware-packager/firmware-packager --- fwupd-1.0.9/contrib/firmware-packager/firmware-packager 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/firmware-packager/firmware-packager 2019-07-15 18:25:54.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/python3 # # Copyright (C) 2017 Max Ehrlich max.ehr@gmail.com # @@ -24,14 +24,14 @@ 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 @@ 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,17 @@ def create_firmware_cab(exe, folder): with cd(folder): - command = ['gcab', '--create', 'firmware.cab', 'firmware.bin', 'firmware.metainfo.xml'] + if os.name == "nt": + directive = os.path.join (folder, "directive") + with open (directive, 'w') as wfd: + wfd.write('.OPTION EXPLICIT\r\n') + wfd.write('.Set CabinetNameTemplate=firmware.cab\r\n') + wfd.write('.Set DiskDirectory1=.\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 +105,17 @@ 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 -Nru fwupd-1.0.9/contrib/firmware-packager/meson.build fwupd-1.2.10/contrib/firmware-packager/meson.build --- fwupd-1.0.9/contrib/firmware-packager/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/firmware-packager/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,2 +1,4 @@ -install_data('firmware-packager', - install_dir : 'share/fwupd') +if get_option('firmware-packager') + install_data('firmware-packager', + install_dir : 'share/fwupd') +endif diff -Nru fwupd-1.0.9/contrib/firmware-packager/README.md fwupd-1.2.10/contrib/firmware-packager/README.md --- fwupd-1.0.9/contrib/firmware-packager/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/firmware-packager/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -26,24 +26,22 @@ ## 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. +`--release-description` Description of the firmware release, again this can theoretically include HTML but I didn't try it. `--exe` Executable file to extract firmware from (e.g. `dell-thunderbolt-firmware.exe`) **REQUIRED** diff -Nru fwupd-1.0.9/contrib/fix_translations.py fwupd-1.2.10/contrib/fix_translations.py --- fwupd-1.0.9/contrib/fix_translations.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/fix_translations.py 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/fwupd.spec.in fwupd-1.2.10/contrib/fwupd.spec.in --- fwupd-1.0.9/contrib/fwupd.spec.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/fwupd.spec.in 2019-07-15 18:25:54.000000000 +0000 @@ -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 @@ -10,28 +10,40 @@ %global enable_ci 0 %global enable_tests 1 %global enable_dummy 1 +%global __meson_wrap_mode default -# fwupdate is only available on these arches +# fwupd.efi is only available on these arches %ifarch x86_64 aarch64 %global have_uefi 1 %endif -# libsmbios is only available on x86, and fwupdate is available on just x86_64 +# redfish is only available on this arch +%ifarch x86_64 +%global have_redfish 1 +%endif + +# libsmbios is only available on x86 %ifarch x86_64 %global have_dell 1 %endif +# only available recently +%if 0%{?fedora} >= 30 +%global have_modem_manager 1 +%endif + Summary: Firmware update daemon Name: fwupd Version: #VERSION# Release: 0.#BUILD#%{?alphatag}%{?dist} -License: GPLv2+ +License: LGPLv2+ URL: https://github.com/hughsie/fwupd Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz 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} BuildRequires: libsoup-devel >= %{libsoup_version} @@ -42,11 +54,12 @@ BuildRequires: libarchive-devel BuildRequires: gobject-introspection-devel BuildRequires: gcab +%ifarch %{valgrind_arches} BuildRequires: valgrind BuildRequires: valgrind-devel +%endif BuildRequires: elfutils-libelf-devel BuildRequires: gtk-doc -BuildRequires: libuuid-devel BuildRequires: gnutls-devel BuildRequires: gnutls-utils BuildRequires: meson @@ -55,41 +68,50 @@ BuildRequires: vala BuildRequires: bash-completion +%if 0%{?have_modem_manager} +BuildRequires: ModemManager-glib-devel >= 1.10.0 +BuildRequires: libqmi-devel >= 1.22.0 +%endif + +%if 0%{?have_redfish} +BuildRequires: efivar-devel >= 33 +%endif + %if 0%{?have_uefi} +BuildRequires: efivar-devel >= 33 BuildRequires: python3 python3-cairo python3-gobject python3-pillow BuildRequires: pango-devel BuildRequires: cairo-devel cairo-gobject-devel BuildRequires: freetype BuildRequires: fontconfig -BuildRequires: dejavu-sans-fonts -BuildRequires: adobe-source-han-sans-cn-fonts +BuildRequires: google-noto-sans-cjk-ttc-fonts +BuildRequires: gnu-efi-devel +BuildRequires: pesign %endif %if 0%{?have_dell} -BuildRequires: efivar-devel +BuildRequires: efivar-devel >= 33 BuildRequires: libsmbios-devel >= 2.3.0 %endif -%if 0%{?have_uefi} -BuildRequires: fwupdate-devel >= 7 -%endif - Requires(post): systemd 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: fwupd-labels = %{version}-%{release} 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 -Obsoletes: libdfu < 0.9.8-1 +Obsoletes: libdfu < 1.0.0 +Obsoletes: fwupd-labels < 1.1.0-1 %description fwupd is a daemon to allow session software to update device firmware. @@ -98,20 +120,11 @@ Summary: Development package for %{name} Requires: %{name}%{?_isa} = %{version}-%{release} Obsoletes: libebitdo-devel < 0.7.5-3 -Obsoletes: libdfu-devel < 0.9.8-1 +Obsoletes: libdfu-devel < 1.0.0 %description devel Files for development with %{name}. -%package labels -Summary: Rendered labels for display during system firmware updates. -# BuildArch: noarch is disabled as we can get "different" .BMP files even when -# running the build on the same architecture due to the randomness introduced -# by the TTF files. - -%description labels -Rendered labels for display during system firmware updates. - %package tests Summary: Data files for installed tests BuildArch: noarch @@ -120,7 +133,7 @@ Data files for installed tests. %prep -%setup -q +%autosetup -p1 %build @@ -139,13 +152,19 @@ %else -Dplugin_dummy=false \ %endif + -Dplugin_flashrom=true \ -Dplugin_thunderbolt=true \ +%if 0%{?have_redfish} + -Dplugin_redfish=true \ +%else + -Dplugin_redfish=false \ +%endif %if 0%{?have_uefi} -Dplugin_uefi=true \ - -Dplugin_uefi_labels=true \ + -Dplugin_nvme=true \ %else -Dplugin_uefi=false \ - -Dplugin_uefi_labels=false \ + -Dplugin_nvme=false \ %endif %if 0%{?have_dell} -Dplugin_dell=true \ @@ -154,6 +173,11 @@ -Dplugin_dell=false \ -Dplugin_synaptics=false \ %endif +%if 0%{?have_modem_manager} + -Dplugin_modem_manager=true \ +%else + -Dplugin_modem_manager=false \ +%endif -Dman=true %meson_build @@ -166,46 +190,79 @@ %install %meson_install +# sign fwupd.efi loader +%if 0%{?have_uefi} +%ifarch x86_64 +%global efiarch x64 +%endif +%ifarch aarch64 +%global efiarch aa64 +%endif +%global fwup_efi_fn $RPM_BUILD_ROOT%{_libexecdir}/fwupd/efi/fwupd%{efiarch}.efi +%pesign -s -i %{fwup_efi_fn} -o %{fwup_efi_fn}.signed +%endif + mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg +# delete most files from the subproject +rm ${RPM_BUILD_ROOT}%{_includedir}/libflashrom.h +rm ${RPM_BUILD_ROOT}%{_libdir}/libflashrom.so +rm ${RPM_BUILD_ROOT}%{_libdir}/pkgconfig/libflashrom.pc +rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom + %find_lang %{name} %post -/sbin/ldconfig %systemd_post fwupd.service %preun %systemd_preun fwupd.service %postun -/sbin/ldconfig %systemd_postun_with_restart fwupd.service +%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} %config(noreplace)%{_sysconfdir}/fwupd/uefi.conf %endif +%if 0%{?have_redfish} +%config(noreplace)%{_sysconfdir}/fwupd/redfish.conf +%endif %dir %{_libexecdir}/fwupd %{_libexecdir}/fwupd/fwupd %{_libexecdir}/fwupd/fwupdtool +%{_libexecdir}/fwupd/fwupdagent +%{_libexecdir}/fwupd/fwupdoffline +%if 0%{?have_uefi} +%{_libexecdir}/fwupd/efi/*.efi +%{_libexecdir}/fwupd/efi/*.efi.signed +%{_libexecdir}/fwupd/fwupdate +%endif %{_bindir}/dfu-tool %{_bindir}/fwupdmgr %dir %{_sysconfdir}/fwupd %dir %{_sysconfdir}/fwupd/remotes.d -%config(noreplace)%{_sysconfdir}/fwupd/remotes.d/fwupd.conf +%if 0%{?have_dell} +%config(noreplace)%{_sysconfdir}/fwupd/remotes.d/dell-esrt.conf +%endif %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 %{_datadir}/bash-completion/completions/fwupdmgr %{_datadir}/bash-completion/completions/fwupdtool +%{_datadir}/bash-completion/completions/fwupdagent %{_datadir}/fwupd/metainfo/org.freedesktop.fwupd*.metainfo.xml -%{_datadir}/fwupd/remotes.d/fwupd/metadata.xml +%if 0%{?have_dell} +%{_datadir}/fwupd/remotes.d/dell-esrt/metadata.xml +%endif %{_datadir}/fwupd/remotes.d/vendor/firmware/README.md %{_datadir}/dbus-1/interfaces/org.freedesktop.fwupd.xml %{_datadir}/polkit-1/actions/org.freedesktop.fwupd.policy @@ -214,6 +271,7 @@ %{_datadir}/man/man1/dfu-tool.1.gz %{_datadir}/man/man1/fwupdmgr.1.gz %{_datadir}/metainfo/org.freedesktop.fwupd.metainfo.xml +%{_datadir}/icons/hicolor/scalable/apps/org.freedesktop.fwupd.svg %{_datadir}/fwupd/firmware-packager %{_unitdir}/fwupd-offline-update.service %{_unitdir}/fwupd.service @@ -225,21 +283,40 @@ %{_libdir}/libfwupd*.so.* %{_libdir}/girepository-1.0/Fwupd-2.0.typelib /usr/lib/udev/rules.d/*.rules +/usr/lib/systemd/system-shutdown/fwupd.shutdown %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} %{_libdir}/fwupd-plugins-3/libfu_plugin_dell.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_dell_esrt.so %endif +%{_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 +%if 0%{?have_modem_manager} +%{_libdir}/fwupd-plugins-3/libfu_plugin_modem_manager.so +%endif %{_libdir}/fwupd-plugins-3/libfu_plugin_nitrokey.so +%if 0%{?have_uefi} +%{_libdir}/fwupd-plugins-3/libfu_plugin_nvme.so +%endif +%if 0%{?have_redfish} +%{_libdir}/fwupd-plugins-3/libfu_plugin_redfish.so +%endif +%{_libdir}/fwupd-plugins-3/libfu_plugin_rts54hid.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_rts54hub.so %{_libdir}/fwupd-plugins-3/libfu_plugin_steelseries.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_superio.so %if 0%{?have_dell} %{_libdir}/fwupd-plugins-3/libfu_plugin_synapticsmst.so %endif +%{_libdir}/fwupd-plugins-3/libfu_plugin_synaptics_prometheus.so %if 0%{?enable_dummy} %{_libdir}/fwupd-plugins-3/libfu_plugin_test.so %endif @@ -251,8 +328,15 @@ %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* +%endif + +# eww, but just until the Fedora package ships these... +%{_libdir}/libflashrom.so.1* %files devel %{_datadir}/gir-1.0/Fwupd-2.0.gir @@ -262,19 +346,15 @@ %{_libdir}/libfwupd*.so %{_libdir}/pkgconfig/fwupd.pc -%files labels -%if 0%{?have_uefi} -%{_datadir}/locale/*/LC_IMAGES/fwupd* -%endif - %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 -Nru fwupd-1.0.9/contrib/nvme-parse.py fwupd-1.2.10/contrib/nvme-parse.py --- fwupd-1.0.9/contrib/nvme-parse.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/nvme-parse.py 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,128 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1+ + +import csv +import binascii +import os +import struct +import glob +from collections import namedtuple + +class Record(object): + def __init__(self, filename, cns): + self.filename = filename + self.cns = cns + +def load_pci_ids(): + pci_vendors = {} + pci_vendors[0x1987] = 'Freescale' + for ln in open('/usr/share/hwdata/pci.ids').read().split('\n'): + if ln.startswith('#'): + continue + if ln.startswith('\t'): + continue + data = ln.split(' ') + if len(data) != 2: + continue + pci_vendors[int(data[0], 16)] = data[1].split(' ')[0] + if data[0] == 'ffff': + break + return pci_vendors + +def _data_to_utf8(s): + return s.decode('utf-8', 'replace').replace('\0', ' ') + +def main(): + + # open files + records = [] + for fn in glob.glob('tests/nvme/*'): + blob = open(fn, 'rb').read() + if len(blob) != 4096: + print('WARNING: ignoring %s of size %i' % (fn, len(blob))) + continue + Cns = namedtuple('Cns', + 'vid ssvid sn mn fr rab ieee cmic mdts cntlid ver ' \ + 'rtd3r rtd3e oaes ctratt rrls rsvd102 oacs acl aerl ' \ + 'frmw lpa elpe npss avscc apsta wctemp cctemp mtfa ' \ + 'hmpre hmmin tnvmcap unvmcap rpmbs edstt dsto fwug ' \ + 'kas hctma mntmt mxtmt sanicap hmminds hmmaxd ' \ + 'nsetidmax rsvd340 anatt anacap anagrpmax nanagrpid ' \ + 'rsvd352 sqes cqes maxcmd nn oncs fuses fna vwc awun ' \ + 'awupf nvscc nwpc acwu rsvd534 sgls mnan rsvd544 ' \ + 'subnqn rsvd1024 ioccsz iorcsz icdoff ctrattr msdbd ' \ + 'rsvd1804 psd vs') + try: + cns = Cns._make(struct.unpack(' 0: + s1ro_cnt += 1 + if (r.cns.frmw & 0x10) >> 4: + fawr_cnt += 1 + nfws = (r.cns.frmw & 0x0e) >> 1 + if nfws in nfws_map: + nfws_map[nfws] += 1 + continue + nfws_map[nfws] = 1 + print('s1ro=%i/%i' % (s1ro_cnt, len(records))) + print('fawr=%i/%i' % (fawr_cnt, len(records))) + nfws = sorted(nfws_map.items(), key=lambda k: k[0], reverse=True) + for nfws, cnt in nfws: + print('nfws[%i]=%i' % (nfws, cnt)) + + # vendor popularity + vids = {} + for r in records: + if r.cns.vid not in vids: + vids[r.cns.vid] = 1 + continue + vids[r.cns.vid] += 1 + vids = sorted(vids.items(), key=lambda k: k[1], reverse=True) + pci_vendors = load_pci_ids() + for vid, cnt in vids: + name = '0x%04x' % vid + if vid in pci_vendors: + name = pci_vendors[vid] + print('%s,%i' % (name, cnt)) + + # vendor records + vs_records = [] + for r in records: + if r.cns.vs: + vs_records.append(r) + print('nr_vs=%i' % len(vs_records)) + +main() diff -Nru fwupd-1.0.9/contrib/org.freedesktop.fwupd.json fwupd-1.2.10/contrib/org.freedesktop.fwupd.json --- fwupd-1.0.9/contrib/org.freedesktop.fwupd.json 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/org.freedesktop.fwupd.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +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", - "tags": [], - "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": "fwupdate", - "buildsystem": "simple", - "build-commands": ["make -C linux prefix=/app libdir=/app/lib", "EFIDIR=/boot/efi make -C linux install prefix=/app libdir=/app/lib"], - "cleanup": [ - "/bin/fwupdate", - "/lib/systemd/system/fwupdate-cleanup.service", - "/libexec/fwupdate/cleanup" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/rhboot/fwupdate/releases/download/11/fwupdate-11.tar.bz2", - "sha256": "d350eae66215c90fdc70f46ea734dedbfe6006ec21b7e764114b7d9e283e4abe" - } - ] - }, - { - "name": "libsmbios_c", - "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": "fwupd", - "buildsystem": "meson", - "config-opts": ["-Dconsolekit=false", - "-Ddaemon=false", - "-Dgpg=false", - "-Dgtkdoc=false", - "-Dintrospection=false", - "-Dman=false", - "-Dpkcs7=false", - "-Dplugin_uefi_labels=false", - "-Dsystemd=false", - "-Dtests=false", - "--sysconfdir=/app/etc", - "--localstatedir=/var/data"], - "cleanup": [ - "/etc/dbus-1/system.d/org.freedesktop.fwupd.conf", - "/share/fwupd/remotes.d/vendor" - ], - "sources": [ - { - "type": "dir", - "skip": [".git"], - "path": ".." - } - ] - } - ] -} diff -Nru fwupd-1.0.9/contrib/PKGBUILD fwupd-1.2.10/contrib/PKGBUILD --- fwupd-1.0.9/contrib/PKGBUILD 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/PKGBUILD 2019-07-15 18:25:54.000000000 +0000 @@ -8,14 +8,16 @@ arch=('i686' 'x86_64') url='https://github.com/hughsie/fwupd' license=('GPL2') -depends=('appstream-glib' 'fwupdate') +depends=('libgusb' 'modemmanager') +optdepends=('tpm2-abrmd' 'tpm2-tools') makedepends=('meson' 'valgrind' 'gobject-introspection' 'gtk-doc' 'python-pillow' 'git' - 'python-cairo' 'ttf-dejavu' 'adobe-source-han-sans-cn-fonts' 'python-gobject' 'vala') + 'python-cairo' 'noto-fonts' 'noto-fonts-cjk' 'python-gobject' 'vala' + 'libsoup' 'polkit' 'gcab') build() { cd ${pkgname} if [ -n "$CI" ]; then - export CI="--werror" + export CI="--werror --wrap-mode=default" fi arch-meson -D b_lto=false $CI ../build diff -Nru fwupd-1.0.9/contrib/README.md fwupd-1.2.10/contrib/README.md --- fwupd-1.0.9/contrib/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -17,7 +17,7 @@ To build the RPMs run this command (from the root of your git checkout): ``` -docker run -t -v `pwd`:/build fwupd-fedora +docker run --privileged -t -v `pwd`:/build fwupd-fedora ``` RPMs will be made available in your working directory when complete. @@ -37,9 +37,9 @@ To build the DEBs run one of these commands (from the root of your git checkout): ``` -docker run -t -v `pwd`:/build fwupd-debian-x86_64 -docker run -t -v `pwd`:/build fwupd-debian-i386 -docker run -t -v `pwd`:/build fwupd-ubuntu-x86_64 +docker run --privileged -t -v `pwd`:/build fwupd-debian-x86_64 +docker run --privileged -t -v `pwd`:/build fwupd-debian-i386 +docker run --privileged -t -v `pwd`:/build fwupd-ubuntu-x86_64 ``` DEBs will be made available in your working directory when complete. diff -Nru fwupd-1.0.9/contrib/simple_client.py fwupd-1.2.10/contrib/simple_client.py --- fwupd-1.0.9/contrib/simple_client.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/simple_client.py 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/snap/activate-shutdown/fwupd-activate.service fwupd-1.2.10/contrib/snap/activate-shutdown/fwupd-activate.service --- fwupd-1.0.9/contrib/snap/activate-shutdown/fwupd-activate.service 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/activate-shutdown/fwupd-activate.service 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,12 @@ +[Unit] +Description=Activate fwupd updates +RequiresMountsFor=/snap/fwupd/current + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStop=/snap/bin/fwupd.fwupdtool activate +SuccessExitStatus=0 2 + +[Install] +WantedBy=multi-user.target diff -Nru fwupd-1.0.9/contrib/snap/activate-shutdown/Makefile fwupd-1.2.10/contrib/snap/activate-shutdown/Makefile --- fwupd-1.0.9/contrib/snap/activate-shutdown/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/activate-shutdown/Makefile 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,9 @@ +build: + true +install: + install -d ${DESTDIR}/etc/systemd/system/ + install -m0644 fwupd-activate.service ${DESTDIR}/etc/systemd/system + # fixes up shutdown activation script for classic snap + sed -i "s,/libexec/fwupd/,/snap/bin/fwupd.," \ + ${SNAPCRAFT_STAGE}/lib/systemd/system-shutdown/fwupd.shutdown + diff -Nru fwupd-1.0.9/contrib/snap/dfu-tool.wrapper fwupd-1.2.10/contrib/snap/dfu-tool.wrapper --- fwupd-1.0.9/contrib/snap/dfu-tool.wrapper 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/dfu-tool.wrapper 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/bin/dfu-tool $@ diff -Nru fwupd-1.0.9/contrib/snap/fix-bash-completion/Makefile fwupd-1.2.10/contrib/snap/fix-bash-completion/Makefile --- fwupd-1.0.9/contrib/snap/fix-bash-completion/Makefile 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/fix-bash-completion/Makefile 2019-07-15 18:25:54.000000000 +0000 @@ -4,4 +4,7 @@ #fixes up fwupdtool -> fwupd.fwupdtool sed -i "s,\(complete -F _fwupd[a-z]*\) \(fwupd.*\),\1 fwupd.\2,; \ s,\(command.*\)\(fwupdtool\),\1fwupd.\2," \ - ${SNAPCRAFT_STAGE}/usr/share/bash-completion/completions/* + ${SNAPCRAFT_STAGE}/share/bash-completion/completions/* + # fixes up dbus service for classic snap + sed -i 's!SystemdService=\(.*\)!SystemdService=snap.fwupd.fwupd.service!' \ + ${SNAPCRAFT_STAGE}/share/dbus-1/system-services/org.freedesktop.fwupd.service diff -Nru fwupd-1.0.9/contrib/snap/fwupd-command fwupd-1.2.10/contrib/snap/fwupd-command --- fwupd-1.0.9/contrib/snap/fwupd-command 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/fwupd-command 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,41 @@ +#!/bin/sh + +export XDG_CACHE_HOME=$SNAP_USER_COMMON/.cache +mkdir -p $XDG_CACHE_HOME +export GIO_MODULE_DIR=$XDG_CACHE_HOME/gio-modules +export XDG_DATA_DIRS="$SNAP/usr/share" + +#determine architecture +if [ "$SNAP_ARCH" = "amd64" ]; then + ARCH="x86_64-linux-gnu" +elif [ "$SNAP_ARCH" = "armhf" ]; then + ARCH="arm-linux-gnueabihf" +elif [ "$SNAP_ARCH" = "arm64" ]; then + ARCH="aarch64-linux-gnu" +else + ARCH="$SNAP_ARCH-linux-gnu" +fi + +# don't update between versions, we want to preserve previous data +[ ! -d "$SNAP_USER_DATA/etc" ] && cp -R "$SNAP/etc" "$SNAP_USER_DATA" +[ ! -d "$SNAP_USER_DATA/var" ] && cp -R "$SNAP/var" "$SNAP_USER_DATA" + +# re-generate gio modules in local cache +needs_update=true +if [ -f $SNAP_USER_DATA/.last_revision ]; then + . $SNAP_USER_DATA/.last_revision 2>/dev/null +fi +if [ "$SNAP_DESKTOP_LAST_REVISION" = "$SNAP_REVISION" ]; then + needs_update=false +fi +if [ $needs_update = true ]; then + if [ -f $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules ]; then + rm -rf $GIO_MODULE_DIR + mkdir -p $GIO_MODULE_DIR + ln -s $SNAP/usr/lib/$ARCH/gio/modules/*.so $GIO_MODULE_DIR + $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules $GIO_MODULE_DIR + fi + echo "SNAP_DESKTOP_LAST_REVISION=$SNAP_REVISION" > $SNAP_USER_DATA/.last_revision +fi + +exec "$@" diff -Nru fwupd-1.0.9/contrib/snap/fwupdmgr.wrapper fwupd-1.2.10/contrib/snap/fwupdmgr.wrapper --- fwupd-1.0.9/contrib/snap/fwupdmgr.wrapper 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/fwupdmgr.wrapper 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/bin/fwupdmgr $@ diff -Nru fwupd-1.0.9/contrib/snap/fwupdtool.wrapper fwupd-1.2.10/contrib/snap/fwupdtool.wrapper --- fwupd-1.0.9/contrib/snap/fwupdtool.wrapper 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/fwupdtool.wrapper 2019-07-15 18:25:54.000000000 +0000 @@ -1,44 +1,2 @@ #!/bin/sh - -export XDG_CACHE_HOME=$SNAP_USER_COMMON/.cache -mkdir -p $XDG_CACHE_HOME -export GIO_MODULE_DIR=$XDG_CACHE_HOME/gio-modules -export XDG_DATA_DIRS="$SNAP/usr/share" - -# don't update between versions, we want to preserve previous data -[ ! -d "$SNAP_USER_DATA/etc" ] && cp -R "$SNAP/etc" "$SNAP_USER_DATA" -[ ! -d "$SNAP_USER_DATA/var" ] && cp -R "$SNAP/var" "$SNAP_USER_DATA" - -#determine architecture -if [ "$SNAP_ARCH" = "amd64" ]; then - ARCH="x86_64-linux-gnu" -elif [ "$SNAP_ARCH" = "armhf" ]; then - ARCH="arm-linux-gnueabihf" -elif [ "$SNAP_ARCH" = "arm64" ]; then - ARCH="aarch64-linux-gnu" -else - ARCH="$SNAP_ARCH-linux-gnu" -fi - -# re-generate gio modules in local cache -needs_update=true -if [ -f $SNAP_USER_DATA/.last_revision ]; then - . $SNAP_USER_DATA/.last_revision 2>/dev/null -fi -if [ "$SNAP_DESKTOP_LAST_REVISION" = "$SNAP_REVISION" ]; then - needs_update=false -fi -if [ $needs_update = true ]; then - if [ -f $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules ]; then - rm -rf $GIO_MODULE_DIR - mkdir -p $GIO_MODULE_DIR - ln -s $SNAP/usr/lib/$ARCH/gio/modules/*.so $GIO_MODULE_DIR - $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules $GIO_MODULE_DIR - fi - echo "SNAP_DESKTOP_LAST_REVISION=$SNAP_REVISION" > $SNAP_USER_DATA/.last_revision -fi - -export PATH="$SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH" -export LD_LIBRARY_PATH="$SNAP/lib:$SNAP/usr/lib:$SNAP/lib/$ARCH:$SNAP/usr/lib/$ARCH" -export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH -exec "$SNAP/usr/lib/fwupd/fwupdtool" "$@" +exec "$SNAP/fwupd-command" $SNAP/libexec/fwupd/fwupdtool $@ diff -Nru fwupd-1.0.9/contrib/snap/fwupd.wrapper fwupd-1.2.10/contrib/snap/fwupd.wrapper --- fwupd-1.0.9/contrib/snap/fwupd.wrapper 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/fwupd.wrapper 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/libexec/fwupd/fwupd $@ diff -Nru fwupd-1.0.9/contrib/snap/fwup-efi-signed/download-fwupd fwupd-1.2.10/contrib/snap/fwup-efi-signed/download-fwupd --- fwupd-1.0.9/contrib/snap/fwup-efi-signed/download-fwupd 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/fwup-efi-signed/download-fwupd 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,36 @@ +#! /usr/bin/python3 + +import re +import shutil +from urllib.parse import urlparse, urlunparse +from urllib.request import urlopen + +import apt +import apt_pkg + +ARCH_TO_EFI_NAME = { + 'amd64': 'x64', + 'i386': 'ia32', + 'arm64': 'aa64', + 'armhf': 'arm', +} +arch = apt_pkg.config['Apt::Architecture'] +efi_name = ARCH_TO_EFI_NAME[arch] +cache = apt.Cache() +fwupd_efi = cache["fwupd"].candidate +pool_parsed = urlparse(fwupd_efi.uri) +dists_dir = "/dists/devel/main/uefi/fwupd-%s/current/" % ( + fwupd_efi.architecture) + +DOWNLOAD_LIST = { + "fwupd%s.efi.signed" %efi_name: "fwupd%s.efi.signed" % efi_name, + "version": "fwupd%s.efi.signed.version" % efi_name +} +for base in DOWNLOAD_LIST: + dists_parsed = list(pool_parsed) + dists_parsed[2] = re.sub(r"/pool/.*", dists_dir + base, dists_parsed[2]) + dists_uri = urlunparse(dists_parsed) + target = DOWNLOAD_LIST[base] + print("Downloading %s to %s..." % (dists_uri, target)) + with urlopen(dists_uri) as dists, open(target, "wb") as out: + shutil.copyfileobj(dists, out) diff -Nru fwupd-1.0.9/contrib/snap/fwup-efi-signed/Makefile fwupd-1.2.10/contrib/snap/fwup-efi-signed/Makefile --- fwupd-1.0.9/contrib/snap/fwup-efi-signed/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/fwup-efi-signed/Makefile 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,34 @@ +DEB_HOST_ARCH=$(shell dpkg-architecture -q DEB_HOST_ARCH) +EFI_NAME := UNKNOWN-EFI-NAME + +ifeq ($(DEB_HOST_ARCH),amd64) +EFI_NAME := x64 +endif + +ifeq ($(DEB_HOST_ARCH),i386) +EFI_NAME := ia32 +endif + +ifeq ($(DEB_HOST_ARCH),arm64) +EFI_NAME := aa64 +endif + +ifeq ($(DEB_HOST_ARCH),armhf) +EFI_NAME := arm +endif + +SIGNED := \ + fwupd$(EFI_NAME).efi.signed + +all: $(SIGNED) + +$(SIGNED): + ./download-fwupd + +install: $(SIGNED) + install -d $(DESTDIR)/libexec/fwupd/efi + install -m0644 $(SIGNED) $(SIGNED).version \ + $(DESTDIR)/libexec/fwupd/efi + +clean: + rm -f $(SIGNED) $(SIGNED).version diff -Nru fwupd-1.0.9/contrib/snap/libefivar-fixpkgconfig/Makefile fwupd-1.2.10/contrib/snap/libefivar-fixpkgconfig/Makefile --- fwupd-1.0.9/contrib/snap/libefivar-fixpkgconfig/Makefile 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/libefivar-fixpkgconfig/Makefile 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -export TRIPLET=$(shell gcc -dumpmachine) - build: true install: - sed -i 's!libdir=\(.*\)!libdir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/usr/lib/${TRIPLET}/pkgconfig/efiboot.pc - sed -i 's!includedir=\(.*\)!includedir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/usr/lib/${TRIPLET}/pkgconfig/efiboot.pc - sed -i 's!Cflags:\(.*\)!Cflags:\1 -L$$\{libdir\}!' ${SNAPCRAFT_STAGE}/usr/lib/${TRIPLET}/pkgconfig/efiboot.pc - sed -i 's!libdir=\(.*\)!libdir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/usr/lib/${TRIPLET}/pkgconfig/efivar.pc - sed -i 's!includedir=\(.*\)!includedir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/usr/lib/${TRIPLET}/pkgconfig/efivar.pc - sed -i 's!Cflags:\(.*\)!Cflags:\1 -L$$\{libdir\}!' ${SNAPCRAFT_STAGE}/usr/lib/${TRIPLET}/pkgconfig/efivar.pc + sed -i 's!libdir=\(.*\)!libdir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/lib/pkgconfig/efiboot.pc + sed -i 's!includedir=\(.*\)!includedir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/lib/pkgconfig/efiboot.pc + sed -i 's!Cflags:\(.*\)!Cflags:\1 -L$$\{libdir\}!' ${SNAPCRAFT_STAGE}/lib/pkgconfig/efiboot.pc + sed -i 's!libdir=\(.*\)!libdir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/lib/pkgconfig/efivar.pc + sed -i 's!includedir=\(.*\)!includedir=${SNAPCRAFT_STAGE}\1!' ${SNAPCRAFT_STAGE}/lib/pkgconfig/efivar.pc + sed -i 's!Cflags:\(.*\)!Cflags:\1 -L$$\{libdir\}!' ${SNAPCRAFT_STAGE}/lib/pkgconfig/efivar.pc diff -Nru fwupd-1.0.9/contrib/snap/snapcraft-master.yaml fwupd-1.2.10/contrib/snap/snapcraft-master.yaml --- fwupd-1.0.9/contrib/snap/snapcraft-master.yaml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/snapcraft-master.yaml 2019-07-15 18:25:54.000000000 +0000 @@ -7,36 +7,41 @@ not yet supported by the version of fwupd distributed with the OS. grade: devel -confinement: devmode +confinement: classic architectures: - amd64 apps: + dfu-tool: + command: dfu-tool.wrapper fwupdtool: command: fwupdtool.wrapper completer: - usr/share/bash-completion/completions/fwupdtool - -plugs: - fwupdtool: - interface: home + 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.git source-type: git build-packages: - libpopt-dev - organize: - usr/lib64: usr/lib/x86_64-linux-gnu - usr/lib: usr/lib/i386-linux-gnu - prime: - - -usr/include - - -usr/bin - - -usr/share/man - - -usr/lib/*/pkgconfig + prime: + - -include + - -bin + - -share/man + - -lib/pkgconfig #adjust the paths from libefivar libefivar-fixpkgconfig: plugin: make @@ -63,35 +68,10 @@ - -share/ - -etc/ - -lib/*.a - libfwup-dev: - plugin: make - source: https://github.com/rhboot/fwupdate.git - source-type: git - make-parameters: - - EFIDIR=ubuntu - - GNUEFIDIR=/usr/lib - - LIBDIR=$SNAPCRAFT_STAGE/usr/lib/x86_64-linux-gnu - - libdir=/usr/lib/x86_64-linux-gnu - - PKG_CONFIG_PATH=$SNAPCRAFT_STAGE/usr/lib/x86_64-linux-gnu/pkgconfig - - --eval=export PKG_CONFIG_PATH - build-packages: - - elfutils - - gnu-efi - - libasm1 - - libdw1 - prime: - - -usr/lib/debug - - -usr/lib/systemd - - -usr/src - - -usr/share - - -usr/lib/*/pkgconfig - - -usr/include - - -usr/bin - after: [libsmbios, libefivar-fixpkgconfig] 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: @@ -100,54 +80,6 @@ - -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 @@ -165,6 +97,50 @@ - -lib/girepository-1.0 - -lib/pkgconfig - -share/ + # this is for the library only, we don't care about the daemon "in-snap" + modemmanager: + plugin: autotools + source: https://gitlab.freedesktop.org/mobile-broadband/ModemManager.git + #not yet tagged + #source-tag: 1.10.0 + after: [gudev, gettext] + # build without these; system daemon needs them + configflags: + - --without-mbim + - --without-qmi + prime: + - -include + - -etc + - -sbin + - -bin + - -share + - -lib/*.*a + - -lib/pkgconfig + - -lib/ModemManager + - -lib/systemd + - -lib/udev + - -lib/girepository-1.0 + libqmi: + plugin: autotools + source: https://gitlab.freedesktop.org/mobile-broadband/libqmi.git + #not yet tagged + #source-tag: 1.10.0 + after: [gudev, gettext, modemmanager] + # build without these; system daemon needs them + configflags: + - --without-udev + prime: + - -include + - -etc + - -sbin + - -bin + - -share + - -lib/*.*a + - -lib/pkgconfig + - -lib/ModemManager + - -lib/systemd + - -lib/udev + - -lib/girepository-1.0 libusb: plugin: autotools source: https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2 @@ -176,60 +152,134 @@ gusb: plugin: meson source: https://github.com/hughsie/libgusb/archive/0.3.0.tar.gz - meson-parameters: [--prefix=/usr, + meson-parameters: [--prefix=/, -Dtests=false, -Dvapi=false, -Ddocs=false] + build-packages: + - libgirepository1.0-dev prime: - - -usr/bin/ + - -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 - - -usr/lib/*/pkgconfig - - -usr/lib/*/girepository-1.0 - after: [libusb] fwupd: plugin: meson - meson-parameters: [--prefix=/usr, - --libexecdir=/usr/lib, + 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=false, + -Ddaemon=true, -Dgtkdoc=false, - -Dplugin_uefi_labels=false, -Dintrospection=false, - -Dsystemd=false, -Dman=false, - -Dconsolekit=false, - -Dpkcs7=false, - -Dgpg=false] + -Dplugin_modem_manager=true, + -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) > $SNAPCRAFT_STAGE/version + echo $(git describe HEAD --always) > $SNAPCRAFT_STAGE/version build-packages: - bash-completion - gcab - - gettext + - 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/lib/gnupg - -usr/share/man - -usr/share/GConf - -etc/X11 @@ -244,13 +294,9 @@ - -usr/lib/*/audit - -usr/share/glib-2.0/schemas - -usr/share/X11 - - -usr/include - - -etc/dbus-1 - - -lib/systemd + - -include - -lib/udev - - -usr/lib/fwupd/fwupd - - -usr/share/dbus-1 - - -usr/share/gnupg + - -lib/*/pkgconfig - -usr/share/lintian - -usr/share/pkgconfig - -usr/share/installed-tests @@ -261,13 +307,16 @@ - -usr/share/info - -usr/share/gir-1.0 - -usr/share/upstart - - -usr/lib/*/glib-2.0 - -usr/lib/*/pkgconfig - after: [appstream-glib-dev, libfwup-dev, gudev, gusb] + after: [gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext, modemmanager, libqmi] fix-bash-completion: plugin: make source: contrib/snap/fix-bash-completion after: [fwupd] + activate-shutdown: + plugin: make + source: contrib/snap/activate-shutdown + after: [fwupd] update-mime: plugin: make source: contrib/snap/update-mime @@ -284,8 +333,12 @@ - -usr/share/pkgconfig - -usr/share/GConf after: [fwupd] - fwupdtool-wrapper: + fwupd-wrappers: plugin: dump source: contrib/snap stage: + - dfu-tool.wrapper + - fwupd-command - fwupdtool.wrapper + - fwupd.wrapper + - fwupdmgr.wrapper diff -Nru fwupd-1.0.9/contrib/snap/snapcraft-stable.yaml fwupd-1.2.10/contrib/snap/snapcraft-stable.yaml --- fwupd-1.0.9/contrib/snap/snapcraft-stable.yaml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/contrib/snap/snapcraft-stable.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,287 +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: devel -confinement: devmode - -architectures: - - amd64 - -apps: - fwupdtool: - command: fwupdtool.wrapper - completer: - usr/share/bash-completion/completions/fwupdtool - -plugs: - fwupdtool: - interface: home - -parts: - libefivar-dev: - plugin: make - source: https://github.com/rhboot/efivar/releases/download/35/efivar-35.tar.bz2 - build-packages: - - libpopt-dev - organize: - usr/lib64: usr/lib/x86_64-linux-gnu - usr/lib: usr/lib/i386-linux-gnu - prime: - - -usr/include - - -usr/bin - - -usr/share/man - - -usr/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 - libfwup-dev: - plugin: make - #fwupdate 11 FTBFS: see https://github.com/rhboot/fwupdate/pull/113 - source: https://github.com/rhboot/fwupdate.git - source-type: git - make-parameters: - - EFIDIR=ubuntu - - GNUEFIDIR=/usr/lib - - LIBDIR=$SNAPCRAFT_STAGE/usr/lib/x86_64-linux-gnu - - libdir=/usr/lib/x86_64-linux-gnu - - PKG_CONFIG_PATH=$SNAPCRAFT_STAGE/usr/lib/x86_64-linux-gnu/pkgconfig - - --eval=export PKG_CONFIG_PATH - build-packages: - - elfutils - - gnu-efi - - libasm1 - - libdw1 - prime: - - -usr/lib/debug - - -usr/lib/systemd - - -usr/src - - -usr/share - - -usr/lib/*/pkgconfig - - -usr/include - - -usr/bin - after: [libsmbios, libefivar-fixpkgconfig] - 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=/usr, - -Dtests=false, - -Dvapi=false, - -Ddocs=false] - prime: - - -usr/bin/ - - -usr/include - - -usr/share - - -usr/lib/*/pkgconfig - - -usr/lib/*/girepository-1.0 - after: [libusb] - fwupd: - plugin: meson - meson-parameters: [--prefix=/usr, - --libexecdir=/usr/lib, - -Dtests=false, - -Ddaemon=false, - -Dgtkdoc=false, - -Dplugin_uefi_labels=false, - -Dintrospection=false, - -Dsystemd=false, - -Dman=false, - -Dconsolekit=false, - -Dpkcs7=false, - -Dgpg=false] - source: . - source-type: git - override-build: | - snapcraftctl build - echo $(git describe HEAD) > $SNAPCRAFT_STAGE/version - build-packages: - - bash-completion - - gcab - - gettext - - libarchive-dev - - libelf-dev - - libgcab-dev - - libglib2.0-dev - - libgpgme11-dev - - libjson-glib-dev - - libsoup2.4-dev - - libsqlite3-dev - - locales - - pkg-config - stage-packages: - - libassuan0 - - liblcms2-2 - - libelf1 - - libgpgme11 - - libjson-glib-1.0-0 - prime: - - -usr/bin - - -usr/sbin - - -usr/lib/gnupg - - -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 - - -usr/include - - -etc/dbus-1 - - -lib/systemd - - -lib/udev - - -usr/lib/fwupd/fwupd - - -usr/share/dbus-1 - - -usr/share/gnupg - - -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/*/glib-2.0 - - -usr/lib/*/pkgconfig - after: [appstream-glib-dev, libfwup-dev, gudev, gusb] - 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] - fwupdtool-wrapper: - plugin: dump - source: contrib/snap - stage: - - fwupdtool.wrapper diff -Nru fwupd-1.0.9/contrib/standalone-installer/assets/header.py fwupd-1.2.10/contrib/standalone-installer/assets/header.py --- fwupd-1.0.9/contrib/standalone-installer/assets/header.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/standalone-installer/assets/header.py 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,319 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Dell, Inc. +# +# SPDX-License-Identifier: LGPL-2.1+ +# +from base64 import b64decode +import io +import os +import subprocess +import sys +import shutil +import tempfile +import zipfile +TAG = b'#\x00' + +def parse_args(): + import argparse + parser = argparse.ArgumentParser(description="Self extracting firmware updater") + parser.add_argument("--directory", help="Directory to extract to") + parser.add_argument("--cleanup", action='store_true', help="Remove tools when done with installation") + parser.add_argument("--verbose", action='store_true', help="Run the tool in verbose mode") + parser.add_argument("--allow-reinstall", action='store_true', help="Allow re-installing existing firmware versions") + parser.add_argument("--allow-older", action='store_true', help="Allow downgrading firmware versions") + parser.add_argument("command", choices=["install", "extract"], help="Command to run") + args = parser.parse_args() + return args + +def error (msg): + print(msg) + sys.exit(1) + +def bytes_slicer(length, source): + start = 0 + stop = length + while start < len(source): + yield source[start:stop] + start = stop + stop += length + +def get_zip(): + script = os.path.realpath (__file__) + bytes_out = io.BytesIO() + with open(script, 'rb') as source: + for line in source: + if not line.startswith(TAG): + continue + bytes_out.write(b64decode(line[len(TAG):-1])) + return bytes_out + +def unzip (destination): + zipf = get_zip () + source = zipfile.ZipFile (zipf, 'r') + for item in source.namelist(): + # extract handles the sanitization + source.extract (item, destination) + +def copy_cabs (source, target): + if not os.path.exists (target): + os.makedirs (target) + cabs = [] + for root, dirs, files in os.walk (source): + for f in files: + if (f.endswith ('.cab')): + origf = os.path.join(root, f) + shutil.copy (origf, target) + cabs.append (os.path.join (target, f)) + return cabs + + +def install_snap (directory, verbose, allow_reinstall, allow_older, uninstall): + app = 'fwupd' + common = '/root/snap/%s/common' % app + + #check if snap is installed + with open(os.devnull, 'w') as devnull: + subprocess.run (['snap'], check=True, stdout=devnull, stderr=devnull) + + #check existing installed + cmd = ['snap', 'list', app] + with open(os.devnull, 'w') as devnull: + if verbose: + print(cmd) + ret = subprocess.run (cmd, stdout=devnull, stderr=devnull) + if ret.returncode == 0: + cmd = ['snap', 'remove', app] + if verbose: + print(cmd) + subprocess.run (cmd, check=True) + + # install the snap + cmd = ['snap', 'ack', os.path.join (directory, 'fwupd.assert')] + if verbose: + print(cmd) + subprocess.run (cmd, check=True) + cmd = ['snap', 'install', '--classic', os.path.join (directory, 'fwupd.snap')] + if verbose: + print(cmd) + subprocess.run (cmd, check=True) + + # copy the CAB files + cabs = copy_cabs (directory, common) + + # run the snap + for cab in cabs: + cmd = ["%s.fwupdmgr" % app, 'install', cab] + if allow_reinstall: + cmd += ["--allow-reinstall"] + if allow_older: + cmd += ["--allow-older"] + if verbose: + cmd += ["--verbose"] + print(cmd) + subprocess.run (cmd) + + #remove copied cabs + for f in cabs: + os.remove(f) + + #cleanup + if uninstall: + cmd = ['snap', 'remove', app] + if verbose: + print(cmd) + subprocess.run (cmd) + +def install_flatpak (directory, verbose, allow_reinstall, allow_older, uninstall): + app = 'org.freedesktop.fwupd' + common = '%s/.var/app/%s' % (os.getenv ('HOME'), app) + + with open(os.devnull, 'w') as devnull: + if not verbose: + output = devnull + else: + output = None + #look for dependencies + dep = 'org.gnome.Platform/x86_64/3.30' + repo = 'flathub' + repo_url = 'https://flathub.org/repo/flathub.flatpakrepo' + cmd = ['flatpak', 'info', dep] + if verbose: + print(cmd) + ret = subprocess.run (cmd, stdout=output, stderr=output) + #not installed + if ret.returncode != 0: + #look for remotes + cmd = ['flatpak', 'remote-info', repo, dep] + if verbose: + print(cmd) + ret = subprocess.run (cmd, stdout=output, stderr=output) + #not enabled, enable it + if ret.returncode != 0: + cmd = ['flatpak', 'remote-add', repo, repo_url] + if verbose: + print(cmd) + ret = subprocess.run (cmd, stderr=output) + # install dep + cmd = ['flatpak', 'install', repo, dep] + if verbose: + print(cmd) + ret = subprocess.run (cmd) + + #check existing installed + cmd = ['flatpak', 'info', app] + if verbose: + print(cmd) + ret = subprocess.run (cmd, stdout=output, stderr=output) + if ret.returncode == 0: + cmd = ['flatpak', 'remove', app] + if verbose: + print(cmd) + subprocess.run (cmd, check=True) + + #install the flatpak + cmd = ['flatpak', 'install', os.path.join (directory, 'fwupd.flatpak')] + if verbose: + print(cmd) + subprocess.run (cmd, check=True) + + # copy the CAB files + cabs = copy_cabs (directory, common) + + #run command + for cab in cabs: + cmd = ['flatpak', 'run', app, 'install', cab] + if allow_reinstall: + cmd += ["--allow-reinstall"] + if allow_older: + cmd += ["--allow-older"] + if verbose: + cmd += ["--verbose"] + print(cmd) + subprocess.run (cmd) + + #remove copied cabs + for f in cabs: + os.remove(f) + + #cleanup + if uninstall: + cmd = ['flatpak', 'remove', app] + if verbose: + print(cmd) + subprocess.run (cmd) + +# Check which package to use +# - return False to use packaged version +# - return True for snap/flatpak +def use_included_version(minimum_version): + try: + import apt + except ModuleNotFoundError: + return True + cache = apt.Cache() + pkg = cache.get("fwupd") + version = pkg.installed + if not version: + return True + if minimum_version: + if minimum_version > version: + print("fwupd %s is already installed but this package requires %s" % + (version.version, minimum_version)) + else: + print("New enough fwupd already installed") + return False + else: + print("fwupd %s is installed and must be removed" % version.version) + return remove_packaged_version(pkg, cache) + +def remove_packaged_version(pkg, cache): + res = False + while not res: + res = input("Remove now (Y/N)? ") + if res.lower() == 'n': + return False + if res.lower() == 'y': + break + res = False + pkg.mark_delete() + res = cache.commit() + if not res: + raise Exception("Need to remove packaged version") + return res + +def install_builtin(directory, verbose, allow_reinstall, allow_older): + cabs = [] + for root, dirs, files in os.walk (directory): + for f in files: + if f.endswith('.cab'): + cabs.append(os.path.join(root, f)) + #run command + for cab in cabs: + cmd = ['fwupdmgr', 'install', cab] + if allow_reinstall: + cmd += ["--allow-reinstall"] + if allow_older: + cmd += ["--allow-older"] + if verbose: + cmd += ["--verbose"] + print(cmd) + subprocess.run(cmd) + +def run_installation (directory, verbose, allow_reinstall, allow_older, uninstall): + try_snap = False + try_flatpak = False + + #determine if a minimum version was specified + minimum_path = os.path.join(directory, "minimum") + minimum = None + if os.path.exists(minimum_path): + with open(minimum_path, "r") as rfd: + minimum = rfd.read() + + if use_included_version(minimum): + install_builtin(directory, verbose, allow_reinstall, allow_older) + return + + # determine what self extracting binary has + if os.path.exists (os.path.join (directory, 'fwupd.snap')) and \ + os.path.exists (os.path.join (directory, 'fwupd.assert')): + try_snap = True + if os.path.exists (os.path.join (directory, 'fwupd.flatpak')): + try_flatpak = True + + if try_snap: + try: + install_snap (directory, verbose, allow_reinstall, allow_older, uninstall) + return True + except Exception as _: + if verbose: + print ("Snap installation failed") + if not try_flatpak: + error ("Snap installation failed") + if try_flatpak: + install_flatpak (directory, verbose, allow_reinstall, allow_older, uninstall) + +if __name__ == '__main__': + args = parse_args() + if 'extract' in args.command: + if args.allow_reinstall: + error ("allow-reinstall argument doesn't make sense with command %s" % args.command) + if args.allow_older: + error ("allow-older argument doesn't make sense with command %s" % args.command) + if args.cleanup: + error ("Cleanup argument doesn't make sense with command %s" % args.command) + if args.directory is None: + error ("No directory specified") + if not os.path.exists (args.directory): + print ("Creating %s" % args.directory) + os.makedirs (args.directory) + unzip (args.directory) + else: + if args.directory: + error ("Directory argument %s doesn't make sense with command %s" % (args.directory, args.command)) + if os.getuid() != 0: + error ("This tool must be run as root") + with tempfile.TemporaryDirectory (prefix='fwupd') as target: + unzip (target) + run_installation (target, args.verbose, args.allow_reinstall, args.allow_older, args.cleanup) diff -Nru fwupd-1.0.9/contrib/standalone-installer/make.py fwupd-1.2.10/contrib/standalone-installer/make.py --- fwupd-1.0.9/contrib/standalone-installer/make.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/standalone-installer/make.py 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,138 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Dell, Inc. +# +# SPDX-License-Identifier: LGPL-2.1+ +# +from base64 import b64encode +import io +import os +import subprocess +import shutil +import sys +import tempfile +import zipfile +from assets.header import TAG + +def error (msg): + print(msg) + sys.exit(1) + +def parse_args(): + import argparse + parser = argparse.ArgumentParser(description="Generate a standalone firmware updater") + parser.add_argument("--disable-snap-download", action='store_true', help="Don't download support for snap") + parser.add_argument("--disable-flatpak-download", action='store_true', help="Don't download support for flatpak") + parser.add_argument("--snap-channel", help="Channel to download snap from (optional)") + parser.add_argument("--minimum", help="Use already installed fwupd version if at least this version") + parser.add_argument("cab", help="CAB file or directory containing CAB files to automatically install") + parser.add_argument('target', help='target file to create') + args = parser.parse_args() + return args + +def bytes_slicer(length, source): + start = 0 + stop = length + while start < len(source): + yield source[start:stop] + start = stop + stop += length + +def generate_installer (directory, target): + asset_base = os.path.join (os.path.dirname(os.path.realpath(__file__)), + "assets") + + #header + shutil.copy (os.path.join (asset_base, "header.py"), target) + + #zip file + buffer = io.BytesIO() + archive = zipfile.ZipFile(buffer, "a") + for root, dirs, files in os.walk (directory): + for f in files: + source = os.path.join(root, f) + archive_fname = source.split (directory) [1] + archive.write(source, archive_fname) + if 'DEBUG' in os.environ: + print (archive.namelist()) + archive.close() + + with open (target, 'ab') as bytes_out: + encoded = b64encode(buffer.getvalue()) + for section in bytes_slicer(64, encoded): + bytes_out.write(TAG) + bytes_out.write(section) + bytes_out.write(b'\n') + +def download_snap (directory, channel): + cmd = ['snap', 'download', 'fwupd'] + if channel is not None: + cmd += ['--channel', channel] + if 'DEBUG' in os.environ: + print(cmd) + subprocess.run (cmd, cwd=directory, check=True) + for f in os.listdir (directory): + # the signatures associated with the snap + if f.endswith(".assert"): + shutil.move (os.path.join(directory, f), os.path.join(directory, 'fwupd.assert')) + # the snap binary itself + elif f.endswith(".snap"): + shutil.move (os.path.join(directory, f), os.path.join(directory, 'fwupd.snap')) + +def download_cab_file (directory, uri): + cmd = ['wget', uri] + if 'DEBUG' in os.environ: + print(cmd) + subprocess.run (cmd, cwd=directory, check=True) + +def download_flatpak (directory): + 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) + +if __name__ == '__main__': + args = parse_args() + + if not args.cab.startswith("http"): + local = args.cab + + with tempfile.TemporaryDirectory (prefix='fwupd') as directory: + if local: + if not os.path.exists (local): + error ("%s doesn't exist" % local) + if not os.path.isdir(local): + shutil.copy (local, directory) + else: + for root, dirs, files in os.walk(local): + for f in files: + shutil.copy (os.path.join(root, f), directory) + else: + download_cab_file (directory, args.cab) + + if not args.disable_snap_download: + download_snap (directory, args.snap_channel) + + if not args.disable_flatpak_download: + download_flatpak (directory) + + if args.minimum: + with open(os.path.join(directory, "minimum"), "w") as wfd: + wfd.write(args.minimum) + + generate_installer (directory, args.target) diff -Nru fwupd-1.0.9/contrib/standalone-installer/README.md fwupd-1.2.10/contrib/standalone-installer/README.md --- fwupd-1.0.9/contrib/standalone-installer/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/standalone-installer/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,9 @@ +# Standalone installer + +This is a script that will build a standalone installer around the fwupd snap or flatpak. +This can be used for distributing updates that use fwupd on machines without networking and the needed tools. + +For usage instructions, view: +``` +./make.py --help +``` diff -Nru fwupd-1.0.9/contrib/vscode/build.sh fwupd-1.2.10/contrib/vscode/build.sh --- fwupd-1.0.9/contrib/vscode/build.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/vscode/build.sh 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/vscode/launcher.sh fwupd-1.2.10/contrib/vscode/launcher.sh --- fwupd-1.0.9/contrib/vscode/launcher.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/vscode/launcher.sh 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/vscode/launch.json fwupd-1.2.10/contrib/vscode/launch.json --- fwupd-1.0.9/contrib/vscode/launch.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/vscode/launch.json 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/contrib/vscode/README.md fwupd-1.2.10/contrib/vscode/README.md --- fwupd-1.0.9/contrib/vscode/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/contrib/vscode/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/CONTRIBUTING.md fwupd-1.2.10/CONTRIBUTING.md --- fwupd-1.0.9/CONTRIBUTING.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/CONTRIBUTING.md 2019-07-15 18:25:54.000000000 +0000 @@ -18,7 +18,7 @@ * Prefer descriptive names over abbreviations (unless well-known) and shortening of names. e.g `device` not `dev` - * Single statments inside if/else should not be enclosed by '{}' + * Single statements inside if/else should not be enclosed by '{}' * Use comments to explain why something is being done, but also avoid over-documenting the obvious. Here is an example of useless comment: diff -Nru fwupd-1.0.9/data/90-fwupd-devices.rules fwupd-1.2.10/data/90-fwupd-devices.rules --- fwupd-1.0.9/data/90-fwupd-devices.rules 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/90-fwupd-devices.rules 2019-07-15 18:25:54.000000000 +0000 @@ -21,3 +21,6 @@ # PCI cards with ROM SUBSYSTEM=="pci", TEST=="/sys$devpath/rom", ENV{FWUPD_GUID}="$attr{vendor}:$attr{device}" + +# NVMe hardware +SUBSYSTEM=="nvme", ENV{ID_VENDOR_FROM_DATABASE}=="", IMPORT{builtin}="hwdb --subsystem=pci" diff -Nru fwupd-1.0.9/data/bash-completion/fwupdagent fwupd-1.2.10/data/bash-completion/fwupdagent --- fwupd-1.0.9/data/bash-completion/fwupdagent 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/bash-completion/fwupdagent 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,37 @@ +_fwupdagent_cmd_list=( + 'get-devices' +) + +_fwupdagent_opts=( + '--verbose' +) + +_show_modifiers() +{ + COMPREPLY+=( $(compgen -W '${_fwupdagent_opts[@]}' -- "$cur") ) +} + +_fwupdagent() +{ + local cur prev command + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + command=${COMP_WORDS[1]} + + case $command in + *) + #find first command + if [[ ${COMP_CWORD} = 1 ]]; then + COMPREPLY=( $(compgen -W '${_fwupdagent_cmd_list[@]}' -- "$cur") ) + #modifiers for all commands + else + _show_modifiers + fi + ;; + esac + + return 0 +} + +complete -F _fwupdagent fwupdagent diff -Nru fwupd-1.0.9/data/bash-completion/fwupdmgr fwupd-1.2.10/data/bash-completion/fwupdmgr --- fwupd-1.0.9/data/bash-completion/fwupdmgr 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/bash-completion/fwupdmgr 2019-07-15 18:25:54.000000000 +0000 @@ -1,11 +1,12 @@ _fwupdmgr_cmd_list=( - 'build-firmware' + 'activate' 'clear-history' 'clear-offline' 'clear-results' 'disable-remote' 'downgrade' 'enable-remote' + 'get-approved-firmware' 'get-details' 'get-devices' 'get-history' @@ -14,13 +15,12 @@ 'get-results' 'get-topology' 'get-updates' - 'hwids' 'install' - 'install-prepared' + 'modify-config' 'modify-remote' - 'monitor' 'refresh' 'report-history' + 'set-approved-firmware' 'unlock' 'update' 'verify' @@ -40,6 +40,7 @@ '--no-metadata-check' '--no-reboot-check' '--show-all-devices' + '--sign' ) _show_modifiers() @@ -73,7 +74,7 @@ command=${COMP_WORDS[1]} case $command in - clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update) + activate|clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update) if [[ "$prev" = "$command" ]]; then _show_device_ids else @@ -147,23 +148,6 @@ else _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 diff -Nru fwupd-1.0.9/data/bash-completion/fwupdtool.in fwupd-1.2.10/data/bash-completion/fwupdtool.in --- fwupd-1.0.9/data/bash-completion/fwupdtool.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/bash-completion/fwupdtool.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,23 +1,35 @@ _fwupdtool_cmd_list=( + 'activate' + 'build-firmware' + 'get-updates' 'get-details' 'get-devices' + 'get-history' 'get-plugins' 'get-topology' + 'hwids' + 'update' 'install' 'install-blob' + 'monitor' + 'self-sign' 'smbios-dump' 'attach' 'detach' + 'verify-update' 'watch' ) _fwupdtool_opts=( '--verbose' + '--enable-json-state' '--allow-reinstall' '--allow-older' '--force' '--show-all-devices' '--plugin-whitelist' + '--prepare' + '--cleanup' ) _show_plugins() @@ -57,13 +69,30 @@ _show_modifiers fi ;; - attach|detach) + attach|detach|activate|verify-update) if [[ "$prev" = "$command" ]]; then _show_device_ids #modifiers else _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 diff -Nru fwupd-1.0.9/data/bash-completion/meson.build fwupd-1.2.10/data/bash-completion/meson.build --- fwupd-1.0.9/data/bash-completion/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/bash-completion/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -10,6 +10,12 @@ ) endif +if get_option('agent') + install_data(['fwupdagent'], + install_dir : tgt, + ) +endif + # replace @libexecdir@ fwupdtool_path = join_paths(libexecdir, 'fwupd') con2 = configuration_data() diff -Nru fwupd-1.0.9/data/builder/README.md fwupd-1.2.10/data/builder/README.md --- fwupd-1.0.9/data/builder/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/builder/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -37,7 +37,7 @@ then the plugin can write to this directory and the startup.sh script will be able to access it as the chroot-ed `/boot`. -Firmware `.cab` files using this funtionality should list the `.tar` file: +Firmware `.cab` files using this functionality should list the `.tar` file: diff -Nru fwupd-1.0.9/data/daemon.conf fwupd-1.2.10/data/daemon.conf --- fwupd-1.0.9/data/daemon.conf 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/daemon.conf 2019-07-15 18:25:54.000000000 +0000 @@ -10,3 +10,15 @@ # 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 + +# Comma separated list of domains to log in verbose mode +# If unset, no domains +# If set to FuValue, FuValue domain (same as --domain-verbose=FuValue) +# If set to *, all domains (same as --verbose) +VerboseDomains= diff -Nru fwupd-1.0.9/data/fwupd-offline-update.service.in fwupd-1.2.10/data/fwupd-offline-update.service.in --- fwupd-1.0.9/data/fwupd-offline-update.service.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/fwupd-offline-update.service.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,8 +1,13 @@ [Unit] Description=Updates device firmware whilst offline Documentation=man:fwupdmgr -OnFailure=reboot.target -ConditionPathExists=/var/lib/fwupd/pending.db +ConditionPathExists=@localstatedir@/lib/fwupd/pending.db +DefaultDependencies=false +Requires=sysinit.target dbus.socket +After=sysinit.target system-update-pre.target dbus.socket systemd-journald.socket +Before=shutdown.target system-update.target [Service] -ExecStart=@bindir@/fwupdmgr install-prepared +Type=oneshot +ExecStart=@libexecdir@/fwupd/fwupdoffline +FailureAction=reboot diff -Nru fwupd-1.0.9/data/fwupd.service.in fwupd-1.2.10/data/fwupd.service.in --- fwupd-1.0.9/data/fwupd.service.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/fwupd.service.in 2019-07-15 18:25:54.000000000 +0000 @@ -2,19 +2,15 @@ Description=Firmware update daemon Documentation=https://fwupd.org/ After=dbus.service -Before=gdm.service +Before=display-manager.service [Service] Type=dbus BusName=org.freedesktop.fwupd ExecStart=@libexecdir@/fwupd/fwupd -MemoryDenyWriteExecute=yes PrivateTmp=yes -ProtectControlGroups=yes ProtectHome=yes -ProtectKernelModules=yes ProtectSystem=full RestrictAddressFamilies=AF_NETLINK AF_UNIX -RestrictRealtime=yes -ReadWritePaths=@localstatedir@/lib/fwupd @sysconfdir@/fwupd/remotes.d -@bootdir@ SystemCallFilter=~@mount +@dynamic_options@ diff -Nru fwupd-1.0.9/data/fwupd.shutdown.in fwupd-1.2.10/data/fwupd.shutdown.in --- fwupd-1.0.9/data/fwupd.shutdown.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/fwupd.shutdown.in 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,7 @@ +#!/bin/sh + +# no history database exists +[ -f @localstatedir@/lib/fwupd/pending.db ] || exit 0 + +# activate firmware when we have a read-only filesysten +@libexecdir@/fwupd/fwupdtool activate Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/data/installed-tests/firmware-example.xml.gz and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/data/installed-tests/firmware-example.xml.gz differ diff -Nru fwupd-1.0.9/data/installed-tests/firmware-example.xml.gz.asc fwupd-1.2.10/data/installed-tests/firmware-example.xml.gz.asc --- fwupd-1.0.9/data/installed-tests/firmware-example.xml.gz.asc 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/installed-tests/firmware-example.xml.gz.asc 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/data/installed-tests/fwupdmgr.sh fwupd-1.2.10/data/installed-tests/fwupdmgr.sh --- fwupd-1.0.9/data/installed-tests/fwupdmgr.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/installed-tests/fwupdmgr.sh 2019-07-15 18:25:54.000000000 +0000 @@ -9,8 +9,8 @@ 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 # --- @@ -20,7 +20,7 @@ # --- echo "Getting devices (should be one)..." -fwupdmgr get-devices +fwupdmgr get-devices --no-unreported-check rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi # --- diff -Nru fwupd-1.0.9/data/installed-tests/fwupd-tests.xml fwupd-1.2.10/data/installed-tests/fwupd-tests.xml --- fwupd-1.0.9/data/installed-tests/fwupd-tests.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/installed-tests/fwupd-tests.xml 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/data/installed-tests/hardware.py fwupd-1.2.10/data/installed-tests/hardware.py --- fwupd-1.0.9/data/installed-tests/hardware.py 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/installed-tests/hardware.py 2019-07-15 18:25:54.000000000 +0000 @@ -40,10 +40,11 @@ return cachefn class Test: - def __init__(self, name, guid): + def __init__(self, name, guid, has_runtime=True): self.files = [] self.name = name self.guid = guid + self.has_runtime = has_runtime def run(self): @@ -68,13 +69,14 @@ client.install(dev.get_id(), fn_cache, flags, cancellable) # verify version - dev = _get_by_device_guid(client, self.guid) - if not dev: - raise GLib.Error('Device did not come back: ' + name) - if not dev.get_version(): - raise GLib.Error('No version set after flash for: ' + name) - if dev.get_version() != ver: - raise GLib.Error('Got: ' + dev.get_version() + ', expected: ' + ver) + if self.has_runtime: + dev = _get_by_device_guid(client, self.guid) + if not dev: + raise GLib.Error('Device did not come back: ' + self.name) + if not dev.get_version(): + raise GLib.Error('No version set after flash for: ' + self.name) + if dev.get_version() != ver: + raise GLib.Error('Got: ' + dev.get_version() + ', expected: ' + ver) # FIXME: wait for device to settle? time.sleep(2) @@ -88,7 +90,7 @@ # DFU A3BU XPLAINED Mouse test = Test('DfuXmegaA3BU-Xplained', '80478b9a-3643-5e47-ab0f-ed28abe1019d') - test.add_file('90c381f1c5932a7f9505372305a615ca000e68df-a3bu-xplained123.cab', '1.23') + test.add_file('f5bbeaba1037dce31dd12f349e8148ae35f98b61-a3bu-xplained123.cab', '1.23') test.add_file('24d838541efe0340bf67e1cc5a9b95526e4d3702-a3bu-xplained124.cab', '1.24') tests.append(test) @@ -162,7 +164,7 @@ tests.append(test) # AIAIAI H05 - test = Test('AIAIAI-H05', '7e8318e1-27ae-55e4-a7a7-a35eff60e9bf') + test = Test('AIAIAI-H05', '7e8318e1-27ae-55e4-a7a7-a35eff60e9bf', has_runtime=False) test.add_file('84279d6bab52262080531acac701523604f3e649-AIAIAI-H05-1.6.cab', '1.6') tests.append(test) diff -Nru fwupd-1.0.9/data/installed-tests/meson.build fwupd-1.2.10/data/installed-tests/meson.build --- fwupd-1.0.9/data/installed-tests/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/installed-tests/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -13,8 +13,7 @@ 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 @@ 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 -Nru fwupd-1.0.9/data/installed-tests/README.md fwupd-1.2.10/data/installed-tests/README.md --- fwupd-1.0.9/data/installed-tests/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/installed-tests/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/data/installed-tests/remote.conf.in fwupd-1.2.10/data/installed-tests/remote.conf.in --- fwupd-1.0.9/data/installed-tests/remote.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/installed-tests/remote.conf.in 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/data/meson.build fwupd-1.2.10/data/meson.build --- fwupd-1.0.9/data/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -3,8 +3,10 @@ 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 @@ -16,12 +18,12 @@ install_dir: join_paths(datadir, 'metainfo') ) -install_data(['org.freedesktop.fwupd.conf'], - install_dir : join_paths(sysconfdir, 'dbus-1', 'system.d') +install_data(['org.freedesktop.fwupd.svg'], + install_dir : join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps') ) -install_data(['metadata.xml'], - install_dir : join_paths(datadir, 'fwupd', 'remotes.d', 'fwupd') +install_data(['org.freedesktop.fwupd.conf'], + install_dir : join_paths(sysconfdir, 'dbus-1', 'system.d') ) if get_option('daemon') @@ -30,26 +32,35 @@ ) endif -con2 = configuration_data() -con2.set('libexecdir', libexecdir) -con2.set('bindir', bindir) -con2.set('localstatedir', localstatedir) -con2.set('datadir', datadir) -con2.set('bootdir', get_option('bootdir')) -con2.set('sysconfdir', default_sysconfdir) - -# replace @libexecdir@ -configure_file( - input : 'org.freedesktop.fwupd.service.in', - output : 'org.freedesktop.fwupd.service', - configuration : con2, - install: true, - install_dir: join_paths(datadir, - 'dbus-1', - 'system-services'), -) - if get_option('systemd') + con2 = configuration_data() + con2.set('libexecdir', libexecdir) + con2.set('bindir', bindir) + con2.set('datadir', datadir) + con2.set('localstatedir', localstatedir) + + rw_directories = [] + rw_directories += join_paths (localstatedir, 'lib', 'fwupd') + rw_directories += join_paths (sysconfdir, 'fwupd') + rw_directories += join_paths (sysconfdir, 'fwupd', 'remotes.d') + if get_option('plugin_uefi') + rw_directories += ['-/boot/efi', '-/efi/EFI', '-/boot/EFI'] + endif + + dynamic_options = [] + if systemd.version().version_compare('>= 232') + dynamic_options += 'ProtectControlGroups=yes' + dynamic_options += 'ProtectKernelModules=yes' + endif + if systemd.version().version_compare('>= 231') + dynamic_options += 'RestrictRealtime=yes' +# dynamic_options += 'MemoryDenyWriteExecute=yes' + dynamic_options += ['ReadWritePaths=' + ' '.join(rw_directories)] + else + dynamic_options += ['ReadWriteDirectories=' + ' '.join(rw_directories)] + endif + con2.set('dynamic_options', '\n'.join(dynamic_options)) + # replace @bindir@ configure_file( input : 'fwupd-offline-update.service.in', @@ -58,10 +69,8 @@ install: true, install_dir: systemdunitdir, ) -endif -if get_option('systemd') - # replace @localstatedir@, @sysconfdir@ and @bootdir@ + # replace @dynamic_options@ configure_file( input : 'fwupd.service.in', output : 'fwupd.service', @@ -69,4 +78,29 @@ install: true, install_dir: systemdunitdir, ) + + # for activation + configure_file( + input : 'fwupd.shutdown.in', + output : 'fwupd.shutdown', + configuration : con2, + install: true, + install_dir: systemd.get_pkgconfig_variable('systemdshutdowndir'), + ) +endif + +if get_option('systemd') or get_option('elogind') + con2 = configuration_data() + con2.set('libexecdir', libexecdir) + + # replace @libexecdir@ + configure_file( + input : 'org.freedesktop.fwupd.service.in', + output : 'org.freedesktop.fwupd.service', + configuration : con2, + install: true, + install_dir: join_paths(datadir, + 'dbus-1', + 'system-services'), + ) endif diff -Nru fwupd-1.0.9/data/metadata.xml fwupd-1.2.10/data/metadata.xml --- fwupd-1.0.9/data/metadata.xml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/metadata.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ - - - - - - UEFI-dummy-dev0 - - 2d47f29b-83a2-4f31-a2e8-63474f4d4c2e - - UEFI Updates - Enable UEFI Update Functionality - - - -

- Applying this update will enable the UEFI firmware reporting interface on your hardware. -

-

- You will have to restart your computer after this update is installed - to be notified of any pending firmware updates. -

-
-
-
-
- - - - com.via.VL811.firmware - - adbb9034-b577-42c2-a661-1ee4f49ef64c - - VL811 Firmware - Firmware for VIA USB 3.0 hub - VIA - http://www.via.com.tw/ - - - -

This stable release fixes the following problems with USB 3.0:

-
    -
  • Do not wake during transition to S4
  • -
  • Do not drop from Apple USB 3.0 Host during S3/S4 and Device PnP
  • -
  • Do not drop during S3/S4 when connected to native Intel and AMD Hosts
  • -
-

This stable release fixes the following problems with USB 2.0:

-
    -
  • Do not drop from Apple USB 3.0 Host during S3/S4 and Device PnP
  • -
-
-
-
-
- - - - com.via.VL811+.firmware - - 54f84d05-c917-4c50-8b35-44feabaaa323 - - VL811+ Firmware - Firmware for VIA USB 3.0 hub - VIA - http://www.via.com.tw/ - - - - - - - - com.via.VL812.firmware - - cd0314ec-b80f-4d1a-a24f-c409183a8b2d - - VL812 Firmware - Firmware for VIA USB 3.0 hub - VIA - http://www.via.com.tw/ - - - - - - - - com.via.VL812_B2.firmware - - 26470009-97a8-4028-867a-bbbac6ee7bf0 - - VL812 B2 Firmware - Firmware for VIA USB 3.0 hub - VIA - http://www.via.com.tw/ - - - - -
diff -Nru fwupd-1.0.9/data/org.freedesktop.fwupd.metainfo.xml fwupd-1.2.10/data/org.freedesktop.fwupd.metainfo.xml --- fwupd-1.0.9/data/org.freedesktop.fwupd.metainfo.xml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/org.freedesktop.fwupd.metainfo.xml 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,6 @@ - + org.freedesktop.fwupd CC0-1.0 LGPL-2.0+ @@ -21,8 +21,6 @@

https://github.com/hughsie/fwupd/issues - - https://fwupd.org/ https://www.transifex.com/freedesktop/fwupd/ richard_at_hughsie.com @@ -30,4 +28,1253 @@ moderate + + fwupdmgr + + + + +

This release adds the following features:

+
    +
  • Add a new experimental plugin that supports libflashrom
  • +
  • Add a specific error code for the low battery case
  • +
  • Add support for 8bitdo USB Retro Receiver
  • +
  • Export new API to build objects from GVariant blobs
  • +
  • Show a warning when running in UEFI legacy mode
  • +
  • Support a UEFI quirk to disable the use of the UX capsule
  • +
+

This release fixes the following bugs:

+
    +
  • Fix installing synaptics-prometheus config updates
  • +
  • Fix the supported list of Wacom tablets
  • +
  • Never set an empty device name
  • +
  • Prompt for reboot when unlocking on the command line if applicable
  • +
  • Show devices with an UpdateError in get-devices output
  • +
  • Support empty proxy server strings
  • +
  • Try harder to find duplicate UEFI boot entries
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add support for Synaptics Prometheus fingerprint readers
  • +
  • Check if VersionFormat is ambiguous when adding devices
  • +
  • Check the daemon version is at least the client version
  • +
  • Export the version-format used by devices to clients
  • +
  • Set the version format for more device types
  • +
+

This release fixes the following bugs:

+
    +
  • Allow using --force to trigger a duplicate offline update
  • +
  • Be smarter about existing installed fwupd when using standalone-installer
  • +
  • Correctly identify DFU firmware that starts at offset zero
  • +
  • Display the remote warning on the console in an easy-to-read way
  • +
  • Fix a libasan failure when reading a UEFI variable
  • +
  • Never guess the version format from the version string
  • +
  • Only use class-based instance IDs for quirk matching
  • +
  • Prompt the user to shutdown if requried when installing by ID
  • +
  • Reset the forced version during DFU attach and detach
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Allow the fwupdmgr tool to modify the daemon config
  • +
+

This release fixes the following bugs:

+
    +
  • Correctly parse DFU interfaces with extra vendor-specific data
  • +
  • Do not report transient or invalid system failures
  • +
  • Fix problems with the version format checking for some updates
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a component categories to express the firmware type
  • +
  • Add support for 8BitDo M30
  • +
  • Add support for the not-child extension from Logitech
  • +
  • Shut down the daemon if the on-disk binary is replaced
  • +
+

This release fixes the following bugs:

+
    +
  • Blacklist the synapticsmst plugin when using amdgpu
  • +
  • Correct ATA activation functionality to work for all vendors
  • +
  • Implement QMI PDC active config selection for modems
  • +
  • Make an error message clearer when there are no updates available
  • +
  • Match the old or new version number when setting NEEDS_REBOOT
  • +
  • More carefully check the output from tpm2_pcrlist
  • +
  • Recreate the history database if migration failed
  • +
  • Require AC power when updating Thunderbolt devices
  • +
  • Require --force to install a release with a different version format
  • +
  • Save history from firmware installed with fwupdtool
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Add a plugin to support modem hardware
  • +
  • Add support for delayed activation of docks and ATA devices
  • +
  • Add support for reading the SuperIO device checksum and writing to e-flash
  • +
  • Add the fwupdagent binary for use in shell scripts
  • +
  • Allow restricting firmware updates for enterprise use
  • +
  • Allow signing the fwupd report with a client certificate
  • +
  • Use Plymouth when updating offline firmware
  • +
+

This release fixes the following bugs:

+
    +
  • Allow forcing an offline-only update on a live system using --force
  • +
  • Allow running offline updates when in system-update.target
  • +
  • Ask to reboot after scheduling an offline firmware update
  • +
  • Correctly check the new version for devices that replug
  • +
  • Do not fail to start the daemon if tpm2_pcrlist hangs
  • +
  • Do not fail when scheduling more than one update to be run offline
  • +
  • Do not let failing to find DBus prevent fwuptool from starting
  • +
  • Do not schedule an update on battery power if it requires AC power
  • +
  • Include all device checksums in the LVFS report
  • +
  • Rename the shimx64.efi binary for known broken firmware
  • +
  • Upload the UPDATE_INFO entry for the UEFI UX capsule
  • +
+
+
+ + +

This release adds the following features:

+
    +
  • Allow a device to be updated using more than one plugin
  • +
  • Report the DeviceInstanceIDs from fwupdmgr when run as root
  • +
+

This release fixes the following bugs:

+
    +
  • Add an extra check for Dell NVMe drives to avoid false positives
  • +
  • Call composite prepare and cleanup using fwupdtool
  • +
  • Correct handling of CAB files with nested directories
  • +
  • Detect and special case Dell ATA hardware
  • +
  • Do not fail fwupdtool if dbus is unavailable
  • +
  • Do not unconditionally enable Werror for the EFI binary
  • +
  • Fill holes when reading SREC files
  • +
  • Filter the last supported payloads of certain Dell docks
  • +
  • Fix flashing failure with latest Intuos Pro tablet
  • +
  • Fix potential segfault when applying UEFI updates
  • +
  • Fix unifying regression when recovering from failed flash
  • +
+
+
+ + +

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 accessing 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 crash 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 information 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 devices
  • +
+
+
+ + +

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 -Nru fwupd-1.0.9/data/org.freedesktop.fwupd.svg fwupd-1.2.10/data/org.freedesktop.fwupd.svg --- fwupd-1.0.9/data/org.freedesktop.fwupd.svg 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/org.freedesktop.fwupd.svg 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,209 @@ + + + + Adwaita Icon Template + + + + + + + + + + + image/svg+xml + + + + GNOME Design Team + + + + + Adwaita Icon Template + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fwupd + firmwareupdater + diff -Nru fwupd-1.0.9/data/remotes.d/fwupd.conf fwupd-1.2.10/data/remotes.d/fwupd.conf --- fwupd-1.0.9/data/remotes.d/fwupd.conf 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/fwupd.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -[fwupd Remote] - -# this remote provides metadata shipped with the fwupd package -Enabled=true -Title=Core -Keyring=none -MetadataURI=file://@datadir@/fwupd/remotes.d/fwupd/metadata.xml diff -Nru fwupd-1.0.9/data/remotes.d/lvfs.conf fwupd-1.2.10/data/remotes.d/lvfs.conf --- fwupd-1.0.9/data/remotes.d/lvfs.conf 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/lvfs.conf 2019-07-15 18:25:54.000000000 +0000 @@ -7,3 +7,4 @@ MetadataURI=https://cdn.fwupd.org/downloads/firmware.xml.gz ReportURI=https://fwupd.org/lvfs/firmware/report OrderBefore=fwupd +ApprovalRequired=false diff -Nru fwupd-1.0.9/data/remotes.d/lvfs.metainfo.xml fwupd-1.2.10/data/remotes.d/lvfs.metainfo.xml --- fwupd-1.0.9/data/remotes.d/lvfs.metainfo.xml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/lvfs.metainfo.xml 2019-07-15 18:25:54.000000000 +0000 @@ -4,7 +4,7 @@ org.freedesktop.fwupd.remotes.lvfs Linux Vendor Firmware Service (stable firmware) - CC0 + CC0-1.0 diff -Nru fwupd-1.0.9/data/remotes.d/lvfs-testing.conf fwupd-1.2.10/data/remotes.d/lvfs-testing.conf --- fwupd-1.0.9/data/remotes.d/lvfs-testing.conf 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/lvfs-testing.conf 2019-07-15 18:25:54.000000000 +0000 @@ -9,3 +9,4 @@ Username= Password= OrderBefore=lvfs,fwupd +ApprovalRequired=false diff -Nru fwupd-1.0.9/data/remotes.d/lvfs-testing.metainfo.xml fwupd-1.2.10/data/remotes.d/lvfs-testing.metainfo.xml --- fwupd-1.0.9/data/remotes.d/lvfs-testing.metainfo.xml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/lvfs-testing.metainfo.xml 2019-07-15 18:25:54.000000000 +0000 @@ -4,7 +4,7 @@ org.freedesktop.fwupd.remotes.lvfs-testing Linux Vendor Firmware Service (testing firmware) - CC0 + CC0-1.0 diff -Nru fwupd-1.0.9/data/remotes.d/meson.build fwupd-1.2.10/data/remotes.d/meson.build --- fwupd-1.0.9/data/remotes.d/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -33,15 +33,15 @@ con2 = configuration_data() con2.set('datadir', datadir) configure_file( - input : 'fwupd.conf', - output : 'fwupd.conf', + input : 'vendor.conf', + output : 'vendor.conf', configuration : con2, install: true, install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) configure_file( - input : 'vendor.conf', - output : 'vendor.conf', + input : 'vendor-directory.conf', + output : 'vendor-directory.conf', configuration : con2, install: true, install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), diff -Nru fwupd-1.0.9/data/remotes.d/README.md fwupd-1.2.10/data/remotes.d/README.md --- fwupd-1.0.9/data/remotes.d/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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,13 +39,27 @@ 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 ====================== The LVFS currently outputs XML with absolute URI locations, e.g. `http://foo/bar.cab` rather than `bar.cab` -This makes mirroring the master LVFS (or other slave instance) somewhat tricky. +This makes mirroring the master LVFS (or other private instance) somewhat tricky. To work around this issue client remotes can specify `FirmwareBaseURI` to replace the URI of the firmware before it is downloaded. diff -Nru fwupd-1.0.9/data/remotes.d/vendor.conf fwupd-1.2.10/data/remotes.d/vendor.conf --- fwupd-1.0.9/data/remotes.d/vendor.conf 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/vendor.conf 2019-07-15 18:25:54.000000000 +0000 @@ -1,8 +1,8 @@ [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 MetadataURI=file://@datadir@/fwupd/remotes.d/vendor/vendor.xml.gz +ApprovalRequired=false diff -Nru fwupd-1.0.9/data/remotes.d/vendor-directory.conf fwupd-1.2.10/data/remotes.d/vendor-directory.conf --- fwupd-1.0.9/data/remotes.d/vendor-directory.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/remotes.d/vendor-directory.conf 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,8 @@ +[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 +ApprovalRequired=false diff -Nru fwupd-1.0.9/data/tests/fwupd/daemon.conf fwupd-1.2.10/data/tests/fwupd/daemon.conf --- fwupd-1.0.9/data/tests/fwupd/daemon.conf 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/tests/fwupd/daemon.conf 2019-07-15 18:25:54.000000000 +0000 @@ -1,2 +1,3 @@ [fwupd] ArchiveSizeMax=5 +ApprovedFirmware=deadbeef diff -Nru fwupd-1.0.9/data/tests/metadata.xml fwupd-1.2.10/data/tests/metadata.xml --- fwupd-1.0.9/data/tests/metadata.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/tests/metadata.xml 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,14 @@ + + + + org.fwupd.8330a096d9f1af8567c7374cb8403e1ce9cf3163.device + + 2d47f29b-83a2-4f31-a2e8-63474f4d4c2e + + + +

Applying will enable UEFI firmware reporting

+
+
+
+
diff -Nru fwupd-1.0.9/data/tests/quirks.d/merged.quirk fwupd-1.2.10/data/tests/quirks.d/merged.quirk --- fwupd-1.0.9/data/tests/quirks.d/merged.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/tests/quirks.d/merged.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +[USB\VID_0A5C&PID_6412] +Flags = MERGE_ME diff -Nru fwupd-1.0.9/data/tests/quirks.d/tests.quirk fwupd-1.2.10/data/tests/quirks.d/tests.quirk --- fwupd-1.0.9/data/tests/quirks.d/tests.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/tests/quirks.d/tests.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,20 @@ -[fwupd-plugin-test] +[USB\VID_0A5C&PID_6412] +Flags= ignore-runtime -USB\VID_0A5C&PID_6412=ignore-runtime +[USB\VID_FFFF&PID_FFFF] +Flags = -# this is an empty key -USB\VID_FFFF&PID_FFFF= +[ACME Inc.=True] +Test = awesome -# this is a key with a space -ACME Inc.=awesome +[CORP*] +Test = town -# this is a wildcard -CORP*=town +[DeviceInstanceId=USB\VID_0BDA&PID_1100] +Flags = clever +Name = Hub +Children = FuDevice|USB\VID_0763&PID_2806&I2C_01 + +[DeviceInstanceId=USB\VID_0763&PID_2806&I2C_01] +Name = HDMI +Flags = updatable,internal diff -Nru fwupd-1.0.9/data/tests/remotes.d/directory.conf fwupd-1.2.10/data/tests/remotes.d/directory.conf --- fwupd-1.0.9/data/tests/remotes.d/directory.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/data/tests/remotes.d/directory.conf 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,4 @@ +[fwupd Remote] +Enabled=true +Keyring=none +MetadataURI=file:///tmp/fwupd-self-test/var/cache/fwupd diff -Nru fwupd-1.0.9/data/tests/remotes.d/testing.conf fwupd-1.2.10/data/tests/remotes.d/testing.conf --- fwupd-1.0.9/data/tests/remotes.d/testing.conf 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/data/tests/remotes.d/testing.conf 2019-07-15 18:25:54.000000000 +0000 @@ -1,3 +1,4 @@ [fwupd Remote] Enabled=true MetadataURI=file:///tmp/fwupd-self-test/testing.xml +ApprovalRequired=true diff -Nru fwupd-1.0.9/debian/changelog fwupd-1.2.10/debian/changelog --- fwupd-1.0.9/debian/changelog 2018-10-23 17:06:37.000000000 +0000 +++ fwupd-1.2.10/debian/changelog 2020-01-09 08:25:38.000000000 +0000 @@ -1,35 +1,238 @@ -fwupd (1.0.9-0ubuntu2) bionic; urgency=medium +fwupd (1.2.10-1ubuntu2~ubuntu18.04.3) bionic; urgency=medium - * Restrict libsmbios-dev to x86 architectures (LP: #1791999, #1768627, #1719797) + * d/p/0001-dont-semver-conversion.patch, d/p/0001-version-handling.patch + d/p/0001-plain_support_in_version.patch: + backport regression fix that we can install firmware with the same + without --allow-reinstall in command line. the patch already merged + in upstream 1.2.X branch. (LP: #1820768) - -- Mario Limonciello Tue, 23 Oct 2018 11:35:36 -0500 + -- Yuan-Chen Cheng Thu, 09 Jan 2020 16:25:38 +0800 -fwupd (1.0.9-0ubuntu1) bionic; urgency=medium +fwupd (1.2.10-1ubuntu2~ubuntu18.04.2) bionic; urgency=medium - * New upstream 1_0_X point release. (LP: #1791999) - - Add support for more Wacom tablets (Richard Hughes) - - Add support for the Synaptics Panamera chip (Mario Limonciello) - - Add validation for Titan Ridge Thunderbolt devices (Andrei Emeltchenko) - - Use boltd force power API if available (Mario Limonciello) - - Allow flashing Unifying devices in recovery mode (Richard Hughes) - - Allow updating just one specific device from the command line - (Richard Hughes) - - Do not hold hidraw devices open forever (Richard Hughes) - - Do not use 8bitdo bootloader commands after a successful flash (Richard Hughes) - - Don't crash when the Dell alternate device has not been set (Richard Hughes) - - Don't potentially expose user passwords for remotes (Richard Hughes) - - Fix a potential buffer overflow when applying a DFU patch (Richard Hughes) - - Fix a potential segfault in smbios data processing (Mario Limonciello) - - Fix downgrading older releases to devices (Richard Hughes) - - Fix failure to detach Unifying devices when using a slow host controller - (Richard Hughes) - - Fix flashing devices that require a manual replug (Richard Hughes) - - Handle -EAGAIN from the kernel better when reading NVM (Mario Limonciello) - (LP: #1768627) - * Backport fix for updates showing up again in gnome-software while staged. - (LP: #1719797) + * d/p/0001-trivial-libfwupd-skip-tests-if-machine-id-is-empty-t.patch: + - Only check the vendor ID if the device has one set (LP: #1856896) - -- Mario Limonciello Wed, 19 Sep 2018 07:41:15 -0500 + -- Robert Ancell Thu, 19 Dec 2019 13:13:48 +1300 + +fwupd (1.2.10-1ubuntu2~ubuntu18.04.1) bionic; urgency=medium + + * Backport to bionic (LP: #1820768) + - meson-0.45-bc.patch: Fix build with meson 0.45 + + [ Steve Langasek ] + * Drop added Recommends: on bolt which is not in flavor seeds and adds a + new service. + + -- Yuan-Chen Cheng Fri, 25 Oct 2019 16:38:06 +0800 + +fwupd (1.2.10-1ubuntu2) eoan; urgency=medium + + * Backport a patch from upstream to relax expired cert checks + in self tests to fix FTBFS (LP: #184375) + * Add a patch to skip some self tests if /etc/machine-id exists + but is empty (LP: #184375) + + -- Mario Limonciello Tue, 24 Sep 2019 09:24:22 -0500 + +fwupd (1.2.10-1ubuntu1) eoan; urgency=medium + + * Merge with debian unstable. + - Remainining changes: downgrade tpm2-tools/tpm2-abrmd to Suggests. + + -- Mario Limonciello Mon, 15 Jul 2019 14:13:33 -0500 + +fwupd (1.2.10-1) unstable; urgency=medium + + * New upstream version (1.2.10) + + -- Mario Limonciello Mon, 15 Jul 2019 13:58:47 -0500 + +fwupd (1.2.9-1ubuntu1) eoan; urgency=medium + + * Merge with debian unstable. + - Remainining changes: downgrade tpm2-tools/tpm2-abrmd to Suggests. + + -- Mario Limonciello Fri, 12 Jul 2019 16:07:38 -0500 + +fwupd (1.2.9-1) unstable; urgency=medium + + * New upstream version (1.2.9) + + -- Mario Limonciello Fri, 12 Jul 2019 14:25:32 -0500 + +fwupd (1.2.6-1) unstable; urgency=medium + + * New upstream version (1.2.6) + * debian/control: + - Add new build depends related to Modem Manager + + -- Mario Limonciello Mon, 01 Apr 2019 21:18:14 -0500 + +fwupd (1.2.5-2) unstable; urgency=medium + + * debian/gen_signing_json: Update the format of the json metadata to + match new requirements: + + Move all the data under a new top-level "packages" key + + Add an empty "trusted_certs" key - our binaries do not do any + further verification with an embedded key. + + -- Steve McIntyre <93sam@debian.org> Mon, 25 Mar 2019 00:32:07 +0000 + +fwupd (1.2.5-1) unstable; urgency=medium + + * New upstream version (1.2.5) + * Drop all patches, upstream + + -- Mario Limonciello Tue, 26 Feb 2019 16:30:52 -0600 + +fwupd (1.2.4-3) unstable; urgency=medium + + * Backport a patch from master that fixes FTBFS with newer glib + + -- Mario Limonciello Fri, 15 Feb 2019 08:06:55 -0600 + +fwupd (1.2.4-2) unstable; urgency=medium + + * debian: explicitly depend on shared-mime-info + + -- Mario Limonciello Thu, 14 Feb 2019 21:21:46 -0600 + +fwupd (1.2.4-1) unstable; urgency=medium + + * New upstream version + * refresh build dependencies + * Recommends on tpm2 stack to read PCR values + + -- Mario Limonciello Wed, 06 Feb 2019 20:31:24 -0600 + +fwupd (1.1.4-1) unstable; urgency=medium + + * New upstream version + + -- Mario Limonciello Wed, 07 Nov 2018 11:30:14 -0600 + +fwupd (1.1.3-2) unstable; urgency=medium + + * Move location of fwupd-SIGNARCH-signed.install to proper directory + to fix generation of signed packages. + + -- Mario Limonciello Sat, 13 Oct 2018 14:17:07 -0500 + +fwupd (1.1.3-1) unstable; urgency=medium + + * New upstream release. + + -- Mario Limonciello Fri, 12 Oct 2018 13:18:46 -0500 + +fwupd (1.1.2-1) unstable; urgency=medium + + * New upstream release + - Fixes ESP autodetection for autofs (Closes: #906216) + - Adds missing signing bits (Closes: #906599) + * debian/rules: + - Pass -a into dh_missing (Closes: #906357) + * debian/control: + - Recommends for bolt for new thunderbolt power API + - Build depends on Noto fonts instead of Dejavu fonts + * Drop all patches. + + -- Mario Limonciello Mon, 10 Sep 2018 11:42:03 -0500 + +fwupd (1.1.1-1) unstable; urgency=medium + + * New upstream release. + - Adds support for more Synaptics and Intel hardware. + - Fixes firmware update on some UEFI implementations (Closes: #905570) + * debian/ + - contrib: debian: regenerate control on clean + - refresh debian/{control,copyright} for upstream fixes + - drop all patches, upstream. + + -- Mario Limonciello Mon, 13 Aug 2018 08:08:53 -0500 + +fwupd (1.1.0-7) unstable; urgency=medium + + * Correct another syntax error in SB signing template (Closes: #905482) + + -- Mario Limonciello Sun, 05 Aug 2018 08:34:37 -0500 + +fwupd (1.1.0-6) unstable; urgency=medium + + * correct secure boot signing template name (Closes: #905471) + + -- Mario Limonciello Sun, 05 Aug 2018 00:06:30 -0500 + +fwupd (1.1.0-5) unstable; urgency=medium + + * Fix secure boot signing template version string (Closes: #905468) + * Refresh debian/copyright (Closes: #904671) + + -- Mario Limonciello Sat, 04 Aug 2018 22:37:36 -0500 + +fwupd (1.1.0-4) unstable; urgency=medium + + * debian/rules: dynamically install EFI binaries + + -- Mario Limonciello Sat, 04 Aug 2018 19:43:44 -0500 + +fwupd (1.1.0-3) unstable; urgency=medium + + * debian/rules: use pkg-config to determine when to turn on redfish and UEFI + - Fixes FTBFS due to redfish on other architectures. + + -- Mario Limonciello Sat, 04 Aug 2018 17:04:27 -0500 + +fwupd (1.1.0-2) unstable; urgency=medium + + * Fix the filename of the signed archive used for secure boot on Ubuntu + * Only build uefi plugin on supported architectures + + -- Mario Limonciello Sat, 04 Aug 2018 11:27:24 -0500 + +fwupd (1.1.0-1) unstable; urgency=medium + + [ Steve Mcintyre ] + * Initial support for UEFI Secure Boot in Debian infrastructure + + When building, also generate a fwupdate-$ARCH-signed-template package + which contains metadata needed by the Debian signing service. This + will end up being turned into a new source package including a signed + version of the fwupdate binary. + + [ Mario Limonciello ] + * New upstream version (1.1.0) + * Drop patches merged upstream. + * debian/control: + - Add a patch from upstream that will add gnu-efi to dependencies + - No longer recommends for fwupdate as it has been merged into fwupd. + * Adjust infrastructure for fwupdate signed package to be used by fwupd signed + package + + -- Mario Limonciello Thu, 12 Jul 2018 08:28:32 -0500 + +fwupd (1.0.8-1) unstable; urgency=medium + + * New upstream version (1.0.8) + - Adds new fwupdtool + - License is now LGPL 2.1 + - Drops colorhug dependency (built in now) + - refresh symbols + + -- Mario Limonciello Thu, 07 Jun 2018 08:16:22 -0500 + +fwupd (1.0.7-1) unstable; urgency=medium + + * New upstream version (1.0.7) + * /debian changes: + - ignore library-not-linked-against-libc + - Remove unused override in debian/lintian/fwupd + - rename tag for debian/source/lintian-overrides + - Adjust to use https in debian/copyright + - Bump debian/compat to 10 + - Update control version + - update standards version + + -- Mario Limonciello Mon, 30 Apr 2018 13:11:17 -0500 fwupd (1.0.6-2) unstable; urgency=medium @@ -79,7 +282,7 @@ fwupd (1.0.2-1) unstable; urgency=medium * New upstream version - * Drop patch for doing libsmbios on only supported architectures, + * Drop patch for doing libsmbios on only supported architectures, now upstream. -- Mario Limonciello Tue, 28 Nov 2017 09:36:57 -0600 @@ -298,7 +501,7 @@ * Correct a cleanup rule * Drop intltool build dependency * Re-enable PIE for builds - * Add additional build dependencies that will be needed for generating + * Add additional build dependencies that will be needed for generating capsule graphics * debian/control: sort build-dependencies * Drop packaging from debian/, it will be git mv'ed from contrib/ upstream diff -Nru fwupd-1.0.9/debian/compat fwupd-1.2.10/debian/compat --- fwupd-1.0.9/debian/compat 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/compat 2019-12-19 00:13:48.000000000 +0000 @@ -1 +1 @@ -10 +11 diff -Nru fwupd-1.0.9/debian/control fwupd-1.2.10/debian/control --- fwupd-1.0.9/debian/control 2018-10-23 16:35:33.000000000 +0000 +++ fwupd-1.2.10/debian/control 2020-01-09 08:25:38.000000000 +0000 @@ -1,32 +1,34 @@ Source: fwupd Priority: optional -Maintainer: Debian EFI +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Debian EFI Uploaders: Steve McIntyre <93sam@debian.org>, Daniel Jared Dominguez , Matthias Klumpp , Mario Limonciello Build-Depends: bash-completion, - debhelper (>= 10.3), + bubblewrap, + debhelper (>= 11), dh-strip-nondeterminism, fontconfig, - fonts-dejavu, + fonts-noto, gcab, gettext (>= 0.19.8.1), gir1.2-pango-1.0, + gnu-efi [amd64 arm64 armhf i386], gnutls-bin, gnutls-dev, gobject-introspection, gtk-doc-tools, help2man, - libappstream-glib-dev (>= 0.6.9), libarchive-dev, libcairo-dev, libcairo-gobject2, - libefivar-dev [amd64 arm64 armhf armel i386], + libefiboot-dev [amd64 arm64 armhf i386], + libefivar-dev [amd64 arm64 armhf i386], libelf-dev, libfreetype6-dev, - libfwup-dev [amd64 arm64 armhf i386], libgcab-dev, libgirepository1.0-dev, libglib2.0-dev (>= 2.45.8), @@ -34,11 +36,14 @@ libgudev-1.0-dev, libgusb-dev (>= 0.2.9), libjson-glib-dev (>= 1.1.1), + libmm-glib-dev, libpolkit-gobject-1-dev, - libsmbios-dev [amd64 i386], + libqmi-glib-dev, + libsmbios-dev [i386 amd64], libsoup2.4-dev, libsqlite3-dev, libtool-bin, + libxmlb-dev (>= 0.1.5), locales, meson, pkg-config, @@ -46,12 +51,13 @@ python3-gi-cairo, python3-pil, python3-requests, + shared-mime-info, systemd (>= 231), udev, umockdev, valac, valgrind [!ia64 !riscv64 !x32 !mips !sparc64 !sh4 !ppc64 !powerpcspe !hppa !alpha !mips64el !armhf !armel !mipsel !m68k], -Standards-Version: 4.1.4 +Standards-Version: 4.3.0 Section: admin Homepage: https://github.com/hughsie/fwupd Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git @@ -67,8 +73,7 @@ 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. @@ -76,9 +81,13 @@ Package: fwupd Architecture: linux-any Depends: ${misc:Depends}, - ${shlibs:Depends} -Recommends: fwupdate, - python3 + ${shlibs:Depends}, + shared-mime-info +Recommends: python3, + fwupd-signed +Suggests: bolt, + tpm2-tools, + tpm2-abrmd, Breaks: gir1.2-dfu-1.0 (<< 0.9.7-1), libdfu1 (<< 0.9.7-1), libdfu-dev (<< 0.9.7-1) @@ -90,8 +99,7 @@ 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 @@ -113,8 +121,7 @@ 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 @@ -129,8 +136,7 @@ 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 @@ -149,8 +155,7 @@ 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 @@ -166,3 +171,31 @@ . It can be used by packages using the GIRepository format to generate dynamic bindings. + +Package: fwupd-amd64-signed-template +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-i386-signed-template +Architecture: i386 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-armhf-signed-template +Architecture: armhf +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-arm64-signed-template +Architecture: arm64 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. diff -Nru fwupd-1.0.9/debian/control.in fwupd-1.2.10/debian/control.in --- fwupd-1.0.9/debian/control.in 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/control.in 2019-12-19 00:13:48.000000000 +0000 @@ -1,12 +1,13 @@ Source: fwupd Priority: optional -Maintainer: Debian EFI +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Debian EFI Uploaders: Steve McIntyre <93sam@debian.org>, Daniel Jared Dominguez , Matthias Klumpp , Mario Limonciello Build-Depends: %%%DYNAMIC%%% -Standards-Version: 4.1.4 +Standards-Version: 4.3.0 Section: admin Homepage: https://github.com/hughsie/fwupd Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git @@ -22,8 +23,7 @@ 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. @@ -31,9 +31,13 @@ Package: fwupd Architecture: linux-any Depends: ${misc:Depends}, - ${shlibs:Depends} -Recommends: fwupdate, - python3 + ${shlibs:Depends}, + shared-mime-info +Recommends: python3, + fwupd-signed +Suggests: bolt, + tpm2-tools, + tpm2-abrmd, Breaks: gir1.2-dfu-1.0 (<< 0.9.7-1), libdfu1 (<< 0.9.7-1), libdfu-dev (<< 0.9.7-1) @@ -45,8 +49,7 @@ 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 @@ -68,8 +71,7 @@ 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 @@ -84,8 +86,7 @@ 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 @@ -104,8 +105,7 @@ 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 @@ -121,3 +121,31 @@ . It can be used by packages using the GIRepository format to generate dynamic bindings. + +Package: fwupd-amd64-signed-template +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-i386-signed-template +Architecture: i386 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-armhf-signed-template +Architecture: armhf +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. + +Package: fwupd-arm64-signed-template +Architecture: arm64 +Depends: ${shlibs:Depends}, ${misc:Depends}, make | build-essential | dpkg-dev +Description: Template for signed fwupd package + This package is used to control code signing by the Debian signing + service. diff -Nru fwupd-1.0.9/debian/copyright fwupd-1.2.10/debian/copyright --- fwupd-1.0.9/debian/copyright 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/copyright 2020-01-09 08:25:38.000000000 +0000 @@ -3,16 +3,32 @@ Source: https://github.com/hughsie/fwupd Files: * -Copyright: 2015 Richard Hughes +Copyright: Aleksander Morgado + Christian J. Kellner + Dell Inc. + Dell, Inc. + Google, Inc. + Intel Corporation. + Kalev Lember + Mario Limonciello + Max Ehrlich max.ehr@gmail.com + Peichen Huang + Peter Jones + Realtek Semiconductor Corporation + Red Hat, Inc. + Richard Hughes + Ryan Chang + Synaptics + Synaptics Inc License: LGPL-2.1+ -Files: data/tests/colorhug/firmware.metainfo.xml -Copyright: 2015 Richard Hughes +Files: *.metainfo.xml +Copyright: Richard Hughes License: CC0-1.0 Files: debian/* Copyright: 2015 Daniel Jared Dominguez - 2015 Mario Limonciello + 2015-2018 Mario Limonciello License: LGPL-2.1+ License: LGPL-2.1+ diff -Nru fwupd-1.0.9/debian/copyright.in fwupd-1.2.10/debian/copyright.in --- fwupd-1.0.9/debian/copyright.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/copyright.in 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,140 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: fwupd +Source: https://github.com/hughsie/fwupd + +%%%DYNAMIC%%% +Files: *.metainfo.xml +Copyright: Richard Hughes +License: CC0-1.0 + +Files: debian/* +Copyright: 2015 Daniel Jared Dominguez + 2015-2018 Mario Limonciello +License: LGPL-2.1+ + +License: LGPL-2.1+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 2.1 can be found in "/usr/share/common-licenses/LGPL-2.1". + + +License: CC0-1.0 + Creative Commons CC0 1.0 Universal + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL + SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT + RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. + CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE + INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES + RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + . + Statement of Purpose + . + The laws of most jurisdictions throughout the world automatically confer + exclusive Copyright and Related Rights (defined below) upon the creator and + subsequent owner(s) (each and all, an "owner") of an original work of + authorship and/or a database (each, a "Work"). Certain owners wish to + permanently relinquish those rights to a Work for the purpose of contributing + to a commons of creative, cultural and scientific works ("Commons") that the + public can reliably and without fear of later claims of infringement build + upon, modify, incorporate in other works, reuse and redistribute as freely as + possible in any form whatsoever and for any purposes, including without + limitation commercial purposes. These owners may contribute to the Commons to + promote the ideal of a free culture and the further production of creative, + cultural and scientific works, or to gain reputation or greater distribution + for their Work in part through the use and efforts of others. For these and/or + other purposes and motivations, and without any expectation of additional + consideration or compensation, the person associating CC0 with a Work (the + "Affirmer"), to the extent that he or she is an owner of Copyright and Related + Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly + distribute the Work under its terms, with knowledge of his or her Copyright and + Related Rights in the Work and the meaning and intended legal effect of CC0 on + those rights. + . + 1. Copyright and Related Rights. A Work made available under CC0 may be + protected by copyright and related or neighboring rights ("Copyright and + Related Rights"). Copyright and Related Rights include, but are not limited to, + the following: + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, subject + to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data in a + Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal protection + of databases, and under any national implementation thereof, including any + amended or successor version of such directive); and + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + . + 2. Waiver. To the greatest extent permitted by, but not in contravention of, + applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and + unconditionally waives, abandons, and surrenders all of Affirmer's Copyright + and Related Rights and associated claims and causes of action, whether now + known or unknown (including existing as well as future claims and causes of + action), in the Work (i) in all territories worldwide, (ii) for the maximum + duration provided by applicable law or treaty (including future time + extensions), (iii) in any current or future medium and for any number of + copies, and (iv) for any purpose whatsoever, including without limitation + commercial, advertising or promotional purposes (the "Waiver"). + Affirmer makes the Waiver for the benefit of each member of the public at large + and to the detriment of Affirmer's heirs and successors, fully intending that + such Waiver shall not be subject to revocation, rescission, cancellation, + termination, or any other legal or equitable action to disrupt the quiet + enjoyment of the Work by the public as contemplated by Affirmer's express + Statement of Purpose. + . + 3. Public License Fallback. Should any part of the Waiver for any reason be + judged legally invalid or ineffective under applicable law, then the Waiver + shall be preserved to the maximum extent permitted taking into account + Affirmer's express Statement of Purpose. In addition, to the extent the Waiver + is so judged Affirmer hereby grants to each affected person a royalty-free, non + transferable, non sublicensable, non exclusive, irrevocable and unconditional + license to exercise Affirmer's Copyright and Related Rights in the Work (i) in + all territories worldwide, (ii) for the maximum duration provided by + applicable law or treaty (including future time extensions), (iii) in any + current or future medium and for any number of copies, and (iv) for any + purpose whatsoever, including without limitation commercial, advertising or + promotional purposes (the "License"). The License shall be deemed effective + as of the date CC0 was applied by Affirmer to the Work. Should any part of + the License for any reason be judged legally invalid or ineffective under + applicable law, such partial invalidity or ineffectiveness shall not + invalidate the remainder of the License, and in such case Affirmer hereby + affirms that he or she will not (i) exercise any of his or her remaining + Copyright and Related Rights in the Work or (ii) assert any associated claims + and causes of action with respect to the Work, in either case contrary to + Affirmer's express Statement of Purpose. + . + 4. Limitations and Disclaimers. + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or warranties of + any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness for + a particular purpose, non infringement, or the absence of latent or other + defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons that + may apply to the Work or any use thereof, including without limitation any + person's Copyright and Related Rights in the Work. Further, Affirmer disclaims + responsibility for obtaining any necessary consents, permissions or other + rights required for any use of the Work. + d. Affirmer understands and acknowledges that Creative Commons is not a party + to this document and has no duty or obligation with respect to this CC0 or use + of the Work. diff -Nru fwupd-1.0.9/debian/docs fwupd-1.2.10/debian/docs --- fwupd-1.0.9/debian/docs 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/docs 2019-12-19 00:13:48.000000000 +0000 @@ -1 +1 @@ -NEWS + diff -Nru fwupd-1.0.9/debian/fwupd.install fwupd-1.2.10/debian/fwupd.install --- fwupd-1.0.9/debian/fwupd.install 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/fwupd.install 2019-12-19 00:13:48.000000000 +0000 @@ -4,13 +4,17 @@ usr/share/bash-completion usr/share/fwupd/* usr/share/dbus-1/* +usr/share/icons/* usr/share/polkit-1/* usr/share/locale usr/share/metainfo/* usr/lib/*/fwupd +usr/lib/*/fwupdagent +usr/lib/*/fwupdoffline usr/lib/*/fwupdtool usr/share/man/man1/* lib/systemd/system/* +lib/systemd/system-shutdown/* var/lib/fwupd lib/udev/rules.d/* data/daemon.conf etc/fwupd diff -Nru fwupd-1.0.9/debian/fwupd.postinst fwupd-1.2.10/debian/fwupd.postinst --- fwupd-1.0.9/debian/fwupd.postinst 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/fwupd.postinst 2019-12-19 00:13:48.000000000 +0000 @@ -6,4 +6,6 @@ if dpkg-maintscript-helper supports rm_conffile 2>/dev/null; then dpkg-maintscript-helper rm_conffile \ /etc/fwupd.conf 1.0.0~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/fwupd/remotes.d/fwupd.conf 1.2.7~ -- "$@" fi diff -Nru fwupd-1.0.9/debian/fwupd-tests.install fwupd-1.2.10/debian/fwupd-tests.install --- fwupd-1.0.9/debian/fwupd-tests.install 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/fwupd-tests.install 2019-12-19 00:13:48.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/debian/fwupd-tests.postinst fwupd-1.2.10/debian/fwupd-tests.postinst --- fwupd-1.0.9/debian/fwupd-tests.postinst 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/fwupd-tests.postinst 2019-12-19 00:13:48.000000000 +0000 @@ -12,4 +12,12 @@ 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 -Nru fwupd-1.0.9/debian/gbp.conf fwupd-1.2.10/debian/gbp.conf --- fwupd-1.0.9/debian/gbp.conf 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/gbp.conf 2019-12-19 00:13:48.000000000 +0000 @@ -1,5 +1,5 @@ [DEFAULT] -debian-branch = debian +debian-branch = ubuntu upstream-tag = %(version)s [buildpackage] diff -Nru fwupd-1.0.9/debian/gen_signing_changelog fwupd-1.2.10/debian/gen_signing_changelog --- fwupd-1.0.9/debian/gen_signing_changelog 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/gen_signing_changelog 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Generate a changelog file for the signed fwupdate package, based on +# a changelog.in file and other state + +DIR=$1 +SOURCE=$2 +ARCH=$3 +IN="${DIR}/changelog.in" +OUT="${DIR}/changelog" + +# Parse out fields from our changelg entry - want the signing-template +# one to match all the important details where we can +DISTRIBUTION="$(dpkg-parsechangelog | sed -ne 's/^Distribution: \(.*\)/\1/p')" +URGENCY="$(dpkg-parsechangelog | sed -ne 's/^Urgency: \(.*\)/\1/p')" +MAINT="$(dpkg-parsechangelog | sed -ne 's/^Maintainer: \(.*\)/\1/p')" +DATE="$(dpkg-parsechangelog | sed -ne 's/^Date: \(.*\)/\1/p')" + +# If the version ends in "+bXXX", this is a binNMU. We don't want a new +# source package to look like that, so change it to ".bXXX" instead +VERSION="$(dpkg-parsechangelog | sed -ne 's/^Version: \(.*\)/\1/p')" +MANGLED_VERSION="$(echo $VERSION | sed -r 's/-/\+/;s/\+(b[[:digit:]]+)$/.\1/')" + +printf "%s-%s-signed (%s) %s; urgency=%s\n" "${SOURCE}" "${ARCH}" "${MANGLED_VERSION}" "${DISTRIBUTION}" "${URGENCY}" > $OUT +printf "\n" >> $OUT +printf " * Update to %s version %s\n" "${SOURCE}" "${VERSION}" >> $OUT +printf "\n" >> $OUT +printf " -- %s %s\n" "${MAINT}" "${DATE}" >> $OUT +printf "\n" >> $OUT + +cat $IN >> $OUT +rm -f $IN diff -Nru fwupd-1.0.9/debian/gen_signing_json fwupd-1.2.10/debian/gen_signing_json --- fwupd-1.0.9/debian/gen_signing_json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/gen_signing_json 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Generate a json file to go in the the fwupd-signed template +# package. Describes exactly what needs to be signed, and how. + +DIR=$1 +SOURCE=$2 +ARCH=$3 +OUT="$DIR/files.json" + +# What file are we looking to sign? +BINARY=$(find debian/tmp -name '*.efi' | xargs basename) + +# Actually needs full path within the binary deb +BINARY="usr/lib/${SOURCE}/efi/${BINARY}" + +rm -f $OUT + +printf '{\n' >> $OUT +printf ' "packages": {\n' >> $OUT +printf ' "%s": {\n' "${SOURCE}" >> $OUT +printf ' "trusted_certs": [],\n' >> $OUT +printf ' "files": [ \n' >> $OUT +printf ' {"sig_type": "efi", "file": "%s"}\n' "${BINARY}" >> $OUT +printf ' ]\n' >> $OUT +printf ' }\n' >> $OUT +printf ' }\n' >> $OUT +printf '}\n' >> $OUT + diff -Nru fwupd-1.0.9/debian/gir1.2-fwupd-2.0.install fwupd-1.2.10/debian/gir1.2-fwupd-2.0.install --- fwupd-1.0.9/debian/gir1.2-fwupd-2.0.install 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/gir1.2-fwupd-2.0.install 2019-12-19 00:13:48.000000000 +0000 @@ -1 +1 @@ -usr/lib/*/girepository-1.0/Fwupd-2.0.typelib +usr/lib/*/girepository-1.0/*.typelib diff -Nru fwupd-1.0.9/debian/libfwupd2.install fwupd-1.2.10/debian/libfwupd2.install --- fwupd-1.0.9/debian/libfwupd2.install 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/libfwupd2.install 2019-12-19 00:13:48.000000000 +0000 @@ -1 +1 @@ -usr/lib/*/libfwup*.so.* +usr/lib/*/*.so.* diff -Nru fwupd-1.0.9/debian/libfwupd2.symbols fwupd-1.2.10/debian/libfwupd2.symbols --- fwupd-1.0.9/debian/libfwupd2.symbols 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/libfwupd2.symbols 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,253 @@ +libfwupd.so.2 libfwupd2 #MINVER# + LIBFWUPD_0.1.1@LIBFWUPD_0.1.1 1.0.0 + LIBFWUPD_0.7.0@LIBFWUPD_0.7.0 1.0.0 + LIBFWUPD_0.7.1@LIBFWUPD_0.7.1 1.0.0 + LIBFWUPD_0.7.3@LIBFWUPD_0.7.3 1.0.0 + LIBFWUPD_0.8.0@LIBFWUPD_0.8.0 1.0.0 + LIBFWUPD_0.9.2@LIBFWUPD_0.9.2 1.0.0 + LIBFWUPD_0.9.3@LIBFWUPD_0.9.3 1.0.0 + LIBFWUPD_0.9.4@LIBFWUPD_0.9.4 1.0.0 + LIBFWUPD_0.9.5@LIBFWUPD_0.9.5 1.0.0 + LIBFWUPD_0.9.6@LIBFWUPD_0.9.6 1.0.0 + LIBFWUPD_0.9.7@LIBFWUPD_0.9.7 1.0.0 + LIBFWUPD_0.9.8@LIBFWUPD_0.9.8 1.0.0 + LIBFWUPD_1.0.0@LIBFWUPD_1.0.0 1.0.0 + LIBFWUPD_1.0.3@LIBFWUPD_1.0.3 1.0.3 + LIBFWUPD_1.0.4@LIBFWUPD_1.0.4 1.0.4 + LIBFWUPD_1.0.7@LIBFWUPD_1.0.7 1.0.7 + LIBFWUPD_1.0.8@LIBFWUPD_1.0.8 1.0.8 + LIBFWUPD_1.1.0@LIBFWUPD_1.1.0 1.1.0 + LIBFWUPD_1.1.1@LIBFWUPD_1.1.1 1.1.1 + LIBFWUPD_1.1.2@LIBFWUPD_1.1.2 1.1.2 + LIBFWUPD_1.1.3@LIBFWUPD_1.1.3 1.1.3 + LIBFWUPD_1.2.10@LIBFWUPD_1.2.10 1.2.10 + LIBFWUPD_1.2.1@LIBFWUPD_1.2.1 1.2.1 + LIBFWUPD_1.2.2@LIBFWUPD_1.2.2 1.2.2 + LIBFWUPD_1.2.4@LIBFWUPD_1.2.4 1.2.4 + LIBFWUPD_1.2.5@LIBFWUPD_1.2.5 1.2.5 + LIBFWUPD_1.2.6@LIBFWUPD_1.2.6 1.2.6 + LIBFWUPD_1.2.7@LIBFWUPD_1.2.7 1.2.7 + LIBFWUPD_1.2.8@LIBFWUPD_1.2.8 1.2.8 + LIBFWUPD_1.2.9@LIBFWUPD_1.2.9 1.2.9 + fwupd_build_history_report_json@LIBFWUPD_1.0.4 1.0.4 + fwupd_build_machine_id@LIBFWUPD_1.0.4 1.0.4 + fwupd_build_user_agent@LIBFWUPD_1.0.3 1.0.3 + fwupd_checksum_format_for_display@LIBFWUPD_0.9.3 1.0.0 + fwupd_checksum_get_best@LIBFWUPD_0.9.4 1.0.0 + fwupd_checksum_get_by_kind@LIBFWUPD_0.9.4 1.0.0 + fwupd_checksum_guess_kind@LIBFWUPD_0.9.3 1.0.0 + fwupd_client_activate@LIBFWUPD_1.2.6 1.2.6 + fwupd_client_clear_results@LIBFWUPD_0.7.0 1.0.0 + fwupd_client_connect@LIBFWUPD_0.7.1 1.0.0 + fwupd_client_get_approved_firmware@LIBFWUPD_1.2.6 1.2.6 + fwupd_client_get_daemon_version@LIBFWUPD_0.9.6 1.0.0 + fwupd_client_get_details@LIBFWUPD_1.0.0 1.0.0 + fwupd_client_get_device_by_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_client_get_devices@LIBFWUPD_0.9.2 1.0.0 + fwupd_client_get_downgrades@LIBFWUPD_0.9.8 1.0.0 + fwupd_client_get_history@LIBFWUPD_1.0.4 1.0.4 + fwupd_client_get_percentage@LIBFWUPD_0.7.3 1.0.0 + fwupd_client_get_releases@LIBFWUPD_0.9.3 1.0.0 + fwupd_client_get_remote_by_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_client_get_remotes@LIBFWUPD_0.9.3 1.0.0 + fwupd_client_get_results@LIBFWUPD_0.7.0 1.0.0 + fwupd_client_get_status@LIBFWUPD_0.7.3 1.0.0 + fwupd_client_get_tainted@LIBFWUPD_1.2.4 1.2.4 + fwupd_client_get_type@LIBFWUPD_0.7.0 1.0.0 + fwupd_client_get_upgrades@LIBFWUPD_0.9.8 1.0.0 + fwupd_client_install@LIBFWUPD_0.7.0 1.0.0 + fwupd_client_modify_config@LIBFWUPD_1.2.8 1.2.8 + fwupd_client_modify_device@LIBFWUPD_1.0.4 1.0.4 + fwupd_client_modify_remote@LIBFWUPD_0.9.8 1.0.0 + fwupd_client_new@LIBFWUPD_0.7.0 1.0.0 + fwupd_client_self_sign@LIBFWUPD_1.2.6 1.2.6 + fwupd_client_set_approved_firmware@LIBFWUPD_1.2.6 1.2.6 + fwupd_client_unlock@LIBFWUPD_0.7.0 1.0.0 + fwupd_client_update_metadata@LIBFWUPD_1.0.0 1.0.0 + fwupd_client_verify@LIBFWUPD_0.7.0 1.0.0 + fwupd_client_verify_update@LIBFWUPD_0.8.0 1.0.0 + fwupd_device_add_checksum@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_add_flag@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_add_guid@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_add_icon@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_add_instance_id@LIBFWUPD_1.2.5 1.2.5 + fwupd_device_add_release@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_array_from_variant@LIBFWUPD_1.2.10 1.2.10 + fwupd_device_compare@LIBFWUPD_1.1.1 1.1.1 + fwupd_device_flag_from_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_device_flag_to_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_device_from_variant@LIBFWUPD_1.0.0 1.0.0 + fwupd_device_get_checksums@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_created@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_description@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_flags@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_flashes_left@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_guid_default@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_guids@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_icons@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_get_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_install_duration@LIBFWUPD_1.1.3 1.1.3 + fwupd_device_get_instance_ids@LIBFWUPD_1.2.5 1.2.5 + fwupd_device_get_modified@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_name@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_parent@LIBFWUPD_1.0.8 1.0.8 + fwupd_device_get_parent_id@LIBFWUPD_1.0.8 1.0.8 + fwupd_device_get_plugin@LIBFWUPD_1.0.0 1.0.0 + fwupd_device_get_release_default@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_get_releases@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_get_serial@LIBFWUPD_1.1.2 1.1.2 + fwupd_device_get_summary@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_type@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_update_error@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_get_update_message@LIBFWUPD_1.2.4 1.2.4 + fwupd_device_get_update_state@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_get_vendor@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_vendor_id@LIBFWUPD_0.9.4 1.0.0 + fwupd_device_get_version@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_version_bootloader@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_get_version_format@LIBFWUPD_1.2.9 1.2.9 + fwupd_device_get_version_lowest@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_has_flag@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_has_guid@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_has_instance_id@LIBFWUPD_1.2.5 1.2.5 + fwupd_device_incorporate@LIBFWUPD_1.1.0 1.1.0 + fwupd_device_new@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_remove_flag@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_created@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_description@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_flags@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_flashes_left@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_install_duration@LIBFWUPD_1.1.3 1.1.3 + fwupd_device_set_modified@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_name@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_parent@LIBFWUPD_1.0.8 1.0.8 + fwupd_device_set_parent_id@LIBFWUPD_1.0.8 1.0.8 + fwupd_device_set_plugin@LIBFWUPD_1.0.0 1.0.0 + fwupd_device_set_serial@LIBFWUPD_1.1.2 1.1.2 + fwupd_device_set_summary@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_update_error@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_set_update_message@LIBFWUPD_1.2.4 1.2.4 + fwupd_device_set_update_state@LIBFWUPD_0.9.8 1.0.0 + fwupd_device_set_vendor@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_vendor_id@LIBFWUPD_0.9.4 1.0.0 + fwupd_device_set_version@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_version_bootloader@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_set_version_format@LIBFWUPD_1.2.9 1.2.9 + fwupd_device_set_version_lowest@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_to_json@LIBFWUPD_1.2.6 1.2.6 + fwupd_device_to_string@LIBFWUPD_0.9.3 1.0.0 + fwupd_device_to_variant@LIBFWUPD_1.0.0 1.0.0 + fwupd_device_to_variant_full@LIBFWUPD_1.1.2 1.1.2 + fwupd_error_from_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_error_quark@LIBFWUPD_0.1.1 1.0.0 + fwupd_error_to_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_get_os_release@LIBFWUPD_1.0.7 1.0.7 + fwupd_guid_from_string@LIBFWUPD_1.2.5 1.2.5 + fwupd_guid_hash_data@LIBFWUPD_1.2.5 1.2.5 + fwupd_guid_hash_string@LIBFWUPD_1.2.5 1.2.5 + fwupd_guid_is_valid@LIBFWUPD_1.2.5 1.2.5 + fwupd_guid_to_string@LIBFWUPD_1.2.5 1.2.5 + fwupd_keyring_kind_from_string@LIBFWUPD_0.9.7 1.0.0 + fwupd_keyring_kind_to_string@LIBFWUPD_0.9.7 1.0.0 + fwupd_release_add_category@LIBFWUPD_1.2.7 1.2.7 + fwupd_release_add_checksum@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_add_flag@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_add_metadata@LIBFWUPD_1.0.4 1.0.4 + fwupd_release_add_metadata_item@LIBFWUPD_1.0.4 1.0.4 + fwupd_release_array_from_variant@LIBFWUPD_1.2.10 1.2.10 + fwupd_release_flag_from_string@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_flag_to_string@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_from_variant@LIBFWUPD_1.0.0 1.0.0 + fwupd_release_get_appstream_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_categories@LIBFWUPD_1.2.7 1.2.7 + fwupd_release_get_checksums@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_description@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_details_url@LIBFWUPD_1.2.4 1.2.4 + fwupd_release_get_filename@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_flags@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_get_homepage@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_install_duration@LIBFWUPD_1.2.1 1.2.4 + fwupd_release_get_license@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_metadata@LIBFWUPD_1.0.4 1.0.4 + fwupd_release_get_metadata_item@LIBFWUPD_1.0.4 1.0.4 + fwupd_release_get_name@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_protocol@LIBFWUPD_1.2.2 1.2.4 + fwupd_release_get_remote_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_size@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_source_url@LIBFWUPD_1.2.4 1.2.4 + fwupd_release_get_summary@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_trust_flags@LIBFWUPD_0.9.8 1.0.0 + fwupd_release_get_type@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_update_message@LIBFWUPD_1.2.4 1.2.4 + fwupd_release_get_uri@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_vendor@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_get_version@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_has_category@LIBFWUPD_1.2.7 1.2.7 + fwupd_release_has_checksum@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_has_flag@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_new@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_remove_flag@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_set_appstream_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_description@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_details_url@LIBFWUPD_1.2.4 1.2.4 + fwupd_release_set_filename@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_flags@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_set_homepage@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_install_duration@LIBFWUPD_1.2.1 1.2.4 + fwupd_release_set_license@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_name@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_protocol@LIBFWUPD_1.2.2 1.2.4 + fwupd_release_set_remote_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_size@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_source_url@LIBFWUPD_1.2.4 1.2.4 + fwupd_release_set_summary@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_trust_flags@LIBFWUPD_0.9.8 1.0.0 + fwupd_release_set_update_message@LIBFWUPD_1.2.4 1.2.4 + fwupd_release_set_uri@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_vendor@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_set_version@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_to_json@LIBFWUPD_1.2.6 1.2.6 + fwupd_release_to_string@LIBFWUPD_0.9.3 1.0.0 + fwupd_release_to_variant@LIBFWUPD_1.0.0 1.0.0 + fwupd_remote_array_from_variant@LIBFWUPD_1.2.10 1.2.10 + fwupd_remote_build_firmware_uri@LIBFWUPD_0.9.7 1.0.0 + fwupd_remote_from_variant@LIBFWUPD_1.0.0 1.0.0 + fwupd_remote_get_age@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_get_agreement@LIBFWUPD_1.0.7 1.0.7 + fwupd_remote_get_approval_required@LIBFWUPD_1.2.6 1.2.6 + fwupd_remote_get_checksum@LIBFWUPD_1.0.0 1.0.0 + fwupd_remote_get_enabled@LIBFWUPD_0.9.3 1.0.0 + fwupd_remote_get_filename_cache@LIBFWUPD_0.9.6 1.0.0 + fwupd_remote_get_filename_cache_sig@LIBFWUPD_0.9.7 1.0.0 + fwupd_remote_get_filename_source@LIBFWUPD_0.9.8 1.0.0 + fwupd_remote_get_firmware_base_uri@LIBFWUPD_0.9.7 1.0.0 + fwupd_remote_get_id@LIBFWUPD_0.9.3 1.0.0 + fwupd_remote_get_keyring_kind@LIBFWUPD_0.9.7 1.0.0 + fwupd_remote_get_kind@LIBFWUPD_0.9.6 1.0.0 + fwupd_remote_get_metadata_uri@LIBFWUPD_0.9.7 1.0.0 + fwupd_remote_get_metadata_uri_sig@LIBFWUPD_0.9.7 1.0.0 + fwupd_remote_get_order_after@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_get_order_before@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_get_password@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_get_priority@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_get_report_uri@LIBFWUPD_1.0.4 1.0.4 + fwupd_remote_get_title@LIBFWUPD_0.9.8 1.0.0 + fwupd_remote_get_type@LIBFWUPD_0.9.3 1.0.0 + fwupd_remote_get_username@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_kind_from_string@LIBFWUPD_0.9.6 1.0.0 + fwupd_remote_kind_to_string@LIBFWUPD_0.9.6 1.0.0 + fwupd_remote_load_from_filename@LIBFWUPD_0.9.3 1.0.0 + fwupd_remote_new@LIBFWUPD_0.9.3 1.0.0 + fwupd_remote_set_agreement@LIBFWUPD_1.0.7 1.0.7 + fwupd_remote_set_mtime@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_set_priority@LIBFWUPD_0.9.5 1.0.0 + fwupd_remote_to_variant@LIBFWUPD_1.0.0 1.0.0 + fwupd_status_from_string@LIBFWUPD_0.1.1 1.0.0 + fwupd_status_to_string@LIBFWUPD_0.1.1 1.0.0 + fwupd_trust_flag_from_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_trust_flag_to_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_update_state_from_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_update_state_to_string@LIBFWUPD_0.7.0 1.0.0 + fwupd_version_format_from_string@LIBFWUPD_1.2.9 1.2.9 + fwupd_version_format_to_string@LIBFWUPD_1.2.9 1.2.9 diff -Nru fwupd-1.0.9/debian/libfwupd-dev.install fwupd-1.2.10/debian/libfwupd-dev.install --- fwupd-1.0.9/debian/libfwupd-dev.install 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/libfwupd-dev.install 2019-12-19 00:13:48.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/debian/lintian/fwupd fwupd-1.2.10/debian/lintian/fwupd --- fwupd-1.0.9/debian/lintian/fwupd 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/lintian/fwupd 2019-12-19 00:13:48.000000000 +0000 @@ -4,3 +4,7 @@ fwupd binary: systemd-service-file-missing-install-key lib/systemd/system/system-update.target.wants/fwupd-offline-update.service #see debian bug 896012 fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_upower.so +#EFI applications are PE executables +fwupd: executable-not-elf-or-script usr/lib/fwupd/efi/*.efi +fwupd: portable-executable-missing-security-features usr/lib/fwupd/efi/*.efi ASLR DEP/NX +fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_modem_manager.so diff -Nru fwupd-1.0.9/debian/patches/0001-dont-semver-conversion.patch fwupd-1.2.10/debian/patches/0001-dont-semver-conversion.patch --- fwupd-1.0.9/debian/patches/0001-dont-semver-conversion.patch 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/patches/0001-dont-semver-conversion.patch 2020-01-09 08:24:28.000000000 +0000 @@ -0,0 +1,94 @@ +commit dfa9edcc487cc174c475bdc712f3c1965bcdfac3 +Author: Richard Hughes +Date: Wed Dec 4 15:05:12 2019 +0000 + + Do not do semver conversion in fu_common_vercmp() + + We don't know the version format, and so it's impossible to do it reliably -- + just do it in the caller where we *do* know the FuDevice version format. + + Cherry-picked from 1fbcb1a1808adebbe80a9c45f156bb695b02fb43 + +--- a/src/fu-common-version.c ++++ b/src/fu-common-version.c +@@ -347,8 +347,8 @@ + + /** + * fu_common_vercmp: +- * @version_a: the release version, e.g. 1.2.3 +- * @version_b: the release version, e.g. 1.2.3.1 ++ * @version_a: the semver release version, e.g. 1.2.3 ++ * @version_b: the semver release version, e.g. 1.2.3.1 + * + * Compares version numbers for sorting. + * +@@ -360,8 +360,6 @@ + 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; + +@@ -374,10 +372,8 @@ + 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); ++ split_a = g_strsplit (version_a, ".", -1); ++ split_b = g_strsplit (version_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; +--- a/src/fu-install-task.c ++++ b/src/fu-install-task.c +@@ -111,10 +111,11 @@ + { + const gchar *tmp; + const gchar *version; +- const gchar *version_release; ++ const gchar *version_release_raw; + const gchar *version_lowest; + gboolean matches_guid = FALSE; + gint vercmp; ++ g_autofree gchar *version_release = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) provides = NULL; + g_autoptr(XbNode) release = NULL; +@@ -209,8 +210,8 @@ + } + + /* is this a downgrade or re-install */ +- version_release = xb_node_get_attr (release, "version"); +- if (version_release == NULL) { ++ version_release_raw = xb_node_get_attr (release, "version"); ++ if (version_release_raw == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, +@@ -273,6 +274,8 @@ + } + + /* check semver */ ++ version_release = fu_common_version_parse_from_format (version_release_raw, ++ fu_device_get_version_format (self->device)); + vercmp = fu_common_vercmp (version, version_release); + if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { + g_set_error (error, +--- a/src/fu-self-test.c ++++ b/src/fu-self-test.c +@@ -3620,10 +3620,6 @@ + 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); diff -Nru fwupd-1.0.9/debian/patches/0001-fu-engine-Don-t-show-devices-pending-a-reboot-in-Get.patch fwupd-1.2.10/debian/patches/0001-fu-engine-Don-t-show-devices-pending-a-reboot-in-Get.patch --- fwupd-1.0.9/debian/patches/0001-fu-engine-Don-t-show-devices-pending-a-reboot-in-Get.patch 2018-09-19 12:40:27.000000000 +0000 +++ fwupd-1.2.10/debian/patches/0001-fu-engine-Don-t-show-devices-pending-a-reboot-in-Get.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -From 80f6e995f68ad7559c4a386a58e9ee43750bf6aa Mon Sep 17 00:00:00 2001 -From: Mario Limonciello -Date: Thu, 13 Sep 2018 10:06:39 -0500 -Subject: [PATCH] fu-engine: Don't show devices pending a reboot in GetUpgrades - -Fixes part of http://gitlab.gnome.org/GNOME/gnome-software/issues/341 ---- - src/fu-engine.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/src/fu-engine.c b/src/fu-engine.c -index f333f08..c7474aa 100644 ---- a/src/fu-engine.c -+++ b/src/fu-engine.c -@@ -2538,6 +2538,16 @@ fu_engine_get_upgrades (FuEngine *self, const gchar *device_id, GError **error) - if (device == NULL) - return NULL; - -+ /* don't show upgrades again until we reboot */ -+ if (fu_device_get_update_state (device) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { -+ g_set_error (error, -+ FWUPD_ERROR, -+ FWUPD_ERROR_NOTHING_TO_DO, -+ "No upgrades for %s: A reboot is pending", -+ fu_device_get_name (device)); -+ return NULL; -+ } -+ - /* get all the releases for the device */ - releases_tmp = fu_engine_get_releases_for_device (self, device, error); - if (releases_tmp == NULL) --- -2.7.4 - diff -Nru fwupd-1.0.9/debian/patches/0001-Only-check-the-vendor-ID-if-the-device-has-one-set.patch fwupd-1.2.10/debian/patches/0001-Only-check-the-vendor-ID-if-the-device-has-one-set.patch --- fwupd-1.0.9/debian/patches/0001-Only-check-the-vendor-ID-if-the-device-has-one-set.patch 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/patches/0001-Only-check-the-vendor-ID-if-the-device-has-one-set.patch 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,28 @@ +From 004a0624d05211e8436060bb7af6b0c6f2d805a3 Mon Sep 17 00:00:00 2001 +From: Richard Hughes +Date: Mon, 9 Dec 2019 10:30:19 +0000 +Subject: [PATCH] Only check the vendor ID if the device has one set + +This means we don't get a weird error if the metadata sets a vendor-id, but the +device does not. +--- + src/fu-engine.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/fu-engine.c b/src/fu-engine.c +index d07843c3..814d15a6 100644 +--- a/src/fu-engine.c ++++ b/src/fu-engine.c +@@ -1058,7 +1058,8 @@ fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req, + } + + /* vendor ID */ +- if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0) { ++ if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0 && ++ fu_device_get_vendor_id (device) != NULL) { + const gchar *version = fu_device_get_vendor_id (device); + if (!fu_engine_require_vercmp (req, version, &error_local)) { + if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { +-- +2.20.1 + diff -Nru fwupd-1.0.9/debian/patches/0001-plain_support_in_version.patch fwupd-1.2.10/debian/patches/0001-plain_support_in_version.patch --- fwupd-1.0.9/debian/patches/0001-plain_support_in_version.patch 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/patches/0001-plain_support_in_version.patch 2020-01-09 08:24:33.000000000 +0000 @@ -0,0 +1,32 @@ +commit a7434265e671ba330214aaab54a41ad1335015ad +Author: Richard Hughes +Date: Wed Dec 4 15:04:19 2019 +0000 + + trivial: Support 'plain' in fu_common_version_from_uintXX() + + Cherry picked from f21add626 + +diff --git a/src/fu-common-version.c b/src/fu-common-version.c +index e4a11002..0ee73f2a 100644 +--- a/src/fu-common-version.c ++++ b/src/fu-common-version.c +@@ -52,7 +52,8 @@ fu_common_version_from_uint32 (guint32 val, FwupdVersionFormat kind) + (val >> 16) & 0xffff, + val & 0xffff); + } +- if (kind == FWUPD_VERSION_FORMAT_NUMBER) { ++ if (kind == FWUPD_VERSION_FORMAT_NUMBER || ++ kind == FWUPD_VERSION_FORMAT_PLAIN) { + /* AABBCCDD */ + return g_strdup_printf ("%" G_GUINT32_FORMAT, val); + } +@@ -109,7 +110,8 @@ fu_common_version_from_uint16 (guint16 val, FwupdVersionFormat kind) + (guint) (val >> 8) & 0xff, + (guint) val & 0xff); + } +- if (kind == FWUPD_VERSION_FORMAT_NUMBER) { ++ if (kind == FWUPD_VERSION_FORMAT_NUMBER || ++ kind == FWUPD_VERSION_FORMAT_PLAIN) { + return g_strdup_printf ("%" G_GUINT16_FORMAT, val); + } + g_critical ("failed to convert version format %s: %u", diff -Nru fwupd-1.0.9/debian/patches/0001-Relax-the-certificate-time-checks-in-the-self-tests-.patch fwupd-1.2.10/debian/patches/0001-Relax-the-certificate-time-checks-in-the-self-tests-.patch --- fwupd-1.0.9/debian/patches/0001-Relax-the-certificate-time-checks-in-the-self-tests-.patch 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/patches/0001-Relax-the-certificate-time-checks-in-the-self-tests-.patch 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,86 @@ +From b4627629cdaadd1a75d7c650b7c5973fcd18dfb0 Mon Sep 17 00:00:00 2001 +From: Richard Hughes +Date: Thu, 1 Aug 2019 09:45:25 +0100 +Subject: [PATCH] Relax the certificate time checks in the self tests for the + legacy certificate + +One test verifies a firmware with a signature from the old LVFS which was +hosted on secure-lvfs.rhcloud.com and used the original PKCS-7 key. This key +had a two year validity (expiring today, ohh the naivety...) rather than the +newer fwupd.org key which expires in the year 2058. + +For this specific test only, disable the certificate time checks to fix CI. + +Fixes https://github.com/hughsie/fwupd/issues/1264 +--- + src/fu-keyring-pkcs7.c | 10 +++++++++- + src/fu-keyring.h | 6 ++++-- + src/fu-self-test.c | 3 ++- + 3 files changed, 15 insertions(+), 4 deletions(-) + +diff --git a/src/fu-keyring-pkcs7.c b/src/fu-keyring-pkcs7.c +index 6dc944ed..a42feaa7 100644 +--- a/src/fu-keyring-pkcs7.c ++++ b/src/fu-keyring-pkcs7.c +@@ -642,6 +642,14 @@ fu_keyring_pkcs7_verify_data (FuKeyring *keyring, + for (gint i = 0; i < count; i++) { + gnutls_pkcs7_signature_info_st info; + gint64 signing_time = 0; ++ gnutls_certificate_verify_flags verify_flags = 0; ++ ++ /* use with care */ ++ if (flags & FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS) { ++ g_debug ("WARNING: disabling time checks"); ++ verify_flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS; ++ verify_flags |= GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS; ++ } + + /* verify the data against the detached signature */ + if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) { +@@ -652,7 +660,7 @@ fu_keyring_pkcs7_verify_data (FuKeyring *keyring, + 0, /* vdata_size */ + i, /* index */ + &datum, /* data */ +- 0); /* flags */ ++ verify_flags); + } + if (rc < 0) { + g_set_error (error, +diff --git a/src/fu-keyring.h b/src/fu-keyring.h +index 39819ca4..2f20e35e 100644 +--- a/src/fu-keyring.h ++++ b/src/fu-keyring.h +@@ -20,12 +20,14 @@ G_DECLARE_DERIVABLE_TYPE (FuKeyring, fu_keyring, FU, KEYRING, GObject) + * FuKeyringVerifyFlags: + * @FU_KEYRING_VERIFY_FLAG_NONE: No flags set + * @FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT: Use client certificate to verify ++ * @FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS: Disable checking of validity periods + * + * The flags to use when interacting with a keyring + **/ + typedef enum { +- FU_KEYRING_VERIFY_FLAG_NONE = 0, +- FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT = 1 << 1, ++ FU_KEYRING_VERIFY_FLAG_NONE = 0, ++ FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT = 1 << 1, ++ FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS = 1 << 2, + /*< private >*/ + FU_KEYRING_VERIFY_FLAG_LAST + } FuKeyringVerifyFlags; +diff --git a/src/fu-self-test.c b/src/fu-self-test.c +index 363f644e..24b12110 100644 +--- a/src/fu-self-test.c ++++ b/src/fu-self-test.c +@@ -2628,7 +2628,8 @@ fu_keyring_pkcs7_func (void) + g_assert_no_error (error); + g_assert_nonnull (blob_sig); + result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, +- FU_KEYRING_VERIFY_FLAG_NONE, &error); ++ FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS, ++ &error); + g_assert_no_error (error); + g_assert_nonnull (result_pass); + g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), >= , 1502871248); +-- +2.20.1 + diff -Nru fwupd-1.0.9/debian/patches/0001-trivial-libfwupd-skip-tests-if-machine-id-is-empty-t.patch fwupd-1.2.10/debian/patches/0001-trivial-libfwupd-skip-tests-if-machine-id-is-empty-t.patch --- fwupd-1.0.9/debian/patches/0001-trivial-libfwupd-skip-tests-if-machine-id-is-empty-t.patch 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/patches/0001-trivial-libfwupd-skip-tests-if-machine-id-is-empty-t.patch 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,38 @@ +From d0fd614bb9023ea7c8f831fc6bfe122a3dbc9032 Mon Sep 17 00:00:00 2001 +From: Mario Limonciello +Date: Tue, 24 Sep 2019 10:29:22 -0500 +Subject: [PATCH] trivial: libfwupd: skip tests if machine-id is empty too + +Ubuntu's buildds seem to have changed and this is causing test suite +failures. +--- + libfwupd/fwupd-self-test.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +Index: fwupd-1.2.10/libfwupd/fwupd-self-test.c +=================================================================== +--- fwupd-1.2.10.orig/libfwupd/fwupd-self-test.c ++++ fwupd-1.2.10/libfwupd/fwupd-self-test.c +@@ -504,6 +504,8 @@ fwupd_has_system_bus (void) + static void + fwupd_common_machine_hash_func (void) + { ++ gsize sz = 0; ++ g_autofree gchar *buf = NULL; + g_autofree gchar *mhash1 = NULL; + g_autofree gchar *mhash2 = NULL; + g_autoptr(GError) error = NULL; +@@ -512,6 +514,13 @@ fwupd_common_machine_hash_func (void) + g_test_skip ("Missing /etc/machine-id"); + return; + } ++ if (!g_file_get_contents ("/etc/machine-id", &buf, &sz, &error)) ++ return; ++ ++ if (sz == 0) { ++ g_test_skip ("Empty /etc/machine-id"); ++ return; ++ } + + mhash1 = fwupd_build_machine_id ("salt1", &error); + g_assert_no_error (error); diff -Nru fwupd-1.0.9/debian/patches/0001-version-handling.patch fwupd-1.2.10/debian/patches/0001-version-handling.patch --- fwupd-1.0.9/debian/patches/0001-version-handling.patch 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/patches/0001-version-handling.patch 2020-01-09 08:24:37.000000000 +0000 @@ -0,0 +1,103 @@ +commit a59d92fe11637d2a62f71bda9024481cbb9956c5 +Author: Mario Limonciello +Date: Mon Oct 14 08:44:39 2019 -0500 + + fu-engine: Read all releases and convert versions when comparing + + Fixes matching ESRT version to metadata version for UEFI + + Cherry-picked from 91d36095e12f2486c774d38eeabcc5b0ddfb0685 + +diff --git a/src/fu-common-version.c b/src/fu-common-version.c +index cd732a38..e4a11002 100644 +--- a/src/fu-common-version.c ++++ b/src/fu-common-version.c +@@ -214,6 +214,31 @@ fu_common_version_ensure_semver (const gchar *version) + */ + gchar * + fu_common_version_parse (const gchar *version) ++{ ++ return fu_common_version_parse_from_format (version, FWUPD_VERSION_FORMAT_TRIPLET); ++} ++ ++/** ++ * fu_common_version_parse_from_format ++ * @version: A version number ++ * @fmt: A FwupdVersionFormat ++ * ++ * Returns a dotted decimal version string from a version string using fmt. ++ * 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" ++ * ++ */ ++gchar * ++fu_common_version_parse_from_format (const gchar *version, FwupdVersionFormat fmt) + { + const gchar *version_noprefix = version; + gchar *endptr = NULL; +@@ -246,7 +271,7 @@ fu_common_version_parse (const gchar *version) + return g_strdup (version); + if (tmp == 0) + return g_strdup (version); +- return fu_common_version_from_uint32 ((guint32) tmp, FWUPD_VERSION_FORMAT_TRIPLET); ++ return fu_common_version_from_uint32 ((guint32) tmp, fmt); + } + + /** +diff --git a/src/fu-common-version.h b/src/fu-common-version.h +index 7899da2d..c25b37ac 100644 +--- a/src/fu-common-version.h ++++ b/src/fu-common-version.h +@@ -18,6 +18,8 @@ gchar *fu_common_version_from_uint32 (guint32 val, + gchar *fu_common_version_from_uint16 (guint16 val, + FwupdVersionFormat kind); + gchar *fu_common_version_parse (const gchar *version); ++gchar *fu_common_version_parse_from_format (const gchar *version, ++ FwupdVersionFormat fmt); + gchar *fu_common_version_ensure_semver (const gchar *version); + FwupdVersionFormat fu_common_version_guess_format (const gchar *version); + gboolean fu_common_version_verify_format (const gchar *version, +diff --git a/src/fu-engine.c b/src/fu-engine.c +index 814d15a6..0139c8e5 100644 +--- a/src/fu-engine.c ++++ b/src/fu-engine.c +@@ -842,14 +842,27 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) + /* try again with the system metadata */ + if (release == NULL) { + GPtrArray *guids = fu_device_get_guids (device); ++ FwupdVersionFormat fmt = fu_device_get_version_format (device); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index (guids, i); + g_autofree gchar *xpath2 = NULL; ++ g_autoptr(GPtrArray) releases = 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); ++ "../../releases/release", ++ guid); ++ releases = xb_silo_query (self->silo, xpath2, 0, error); ++ if (releases == NULL) ++ return FALSE; ++ for (guint j = 0; j < releases->len; j++) { ++ XbNode *rel = g_ptr_array_index (releases, j); ++ const gchar *rel_ver = xb_node_get_attr (rel, "version"); ++ g_autofree gchar *tmp_ver = fu_common_version_parse_from_format (rel_ver, fmt); ++ if (fu_common_vercmp (tmp_ver, version) == 0) { ++ release = g_object_ref (rel); ++ break; ++ } ++ } + if (release != NULL) + break; + } diff -Nru fwupd-1.0.9/debian/patches/meson-0.45-bc.patch fwupd-1.2.10/debian/patches/meson-0.45-bc.patch --- fwupd-1.0.9/debian/patches/meson-0.45-bc.patch 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/patches/meson-0.45-bc.patch 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,47 @@ +Description: meson 0.45 backward compatibility patch + + * meson 0.45 backward compatibility patch from Mario. + +Author: Yuan-Chen Cheng + +--- +The information above should follow the Patch Tagging Guidelines, please +checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here +are templates for supplementary fields that you might want to add: + +Origin: , +Bug: +Bug-Debian: https://bugs.debian.org/ +Bug-Ubuntu: https://launchpad.net/bugs/ +Forwarded: +Reviewed-By: +Last-Update: 2019-08-01 + +--- fwupd-1.2.10.orig/meson.build ++++ fwupd-1.2.10/meson.build +@@ -1,7 +1,7 @@ + project('fwupd', 'c', + version : '1.2.10', + license : 'LGPL-2.1+', +- meson_version : '>=0.47.0', ++ meson_version : '>=0.44.0', + default_options : ['warning_level=2', 'c_std=c99'], + ) + +@@ -115,8 +115,14 @@ test_link_args = [ + '-Wl,-z,now', + ] + foreach arg: test_link_args +- if cc.has_link_argument(arg) +- global_link_args += arg ++ 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 + endif + endforeach + add_global_link_arguments( diff -Nru fwupd-1.0.9/debian/patches/series fwupd-1.2.10/debian/patches/series --- fwupd-1.0.9/debian/patches/series 2018-09-19 12:40:49.000000000 +0000 +++ fwupd-1.2.10/debian/patches/series 2020-01-09 08:25:01.000000000 +0000 @@ -1 +1,8 @@ -0001-fu-engine-Don-t-show-devices-pending-a-reboot-in-Get.patch +0001-Relax-the-certificate-time-checks-in-the-self-tests-.patch +0001-trivial-libfwupd-skip-tests-if-machine-id-is-empty-t.patch +meson-0.45-bc.patch +0001-Only-check-the-vendor-ID-if-the-device-has-one-set.patch +0001-dont-semver-conversion.patch +0001-version-handling.patch +0001-plain_support_in_version.patch + diff -Nru fwupd-1.0.9/debian/README.Debian fwupd-1.2.10/debian/README.Debian --- fwupd-1.0.9/debian/README.Debian 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/README.Debian 2019-12-19 00:13:48.000000000 +0000 @@ -1,7 +1,18 @@ -fwupd for Debian ----------------- +signed vs unsigned fwupd programs +------------------------------------ -fwupd is still heavily in development. As of this date, the functionality -it provides is not yet available on most systems. +fwupd 1.1.0 is configured to understand when to use a signed version +of the EFI binary. If the signed version isn't installed but secure +boot is turned on, it will avoid copying to the EFI system partition. + +This allows supporting secure boot even if not turned on at install, or +changed later after install. + +In Ubuntu, both fwupd-signed and fwupd are seeded in the default +installation. Nothing is installed to the ESP until it's needed. + +In Debian, the package name for the signed version is slightly +different due to different infrastructure. fwupd-signed-$ARCH and +fwupd should both be installed and then things will work similarly +to what's described above. - -- Daniel Jared Dominguez Wed, 20 May 2015 17:16:02 -0500 diff -Nru fwupd-1.0.9/debian/rules fwupd-1.2.10/debian/rules --- fwupd-1.0.9/debian/rules 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/rules 2019-12-19 00:13:48.000000000 +0000 @@ -3,6 +3,7 @@ export LC_ALL := C.UTF-8 export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_STRIP=-Wl,-Bsymbolic-functions #GPGME needs this for proper building on 32 bit archs ifeq "$(DEB_HOST_ARCH_BITS)" "32" @@ -10,36 +11,84 @@ endif ifneq ($(CI),) - export CI=--werror + export CI=--werror --wrap-mode=default +endif + +regenerate_control: + OS=debian-x86_64 ./contrib/ci/generate_debian.py + +SB_STYLE := debian +deb_version := $(shell dpkg-parsechangelog --show-field Version) +ifeq (yes,$(shell dpkg-vendor --derives-from Ubuntu && echo yes)) + SB_STYLE := ubuntu + tar_name := fwupd_$(deb_version)_$(DEB_HOST_ARCH).tar.gz +else + TMPLDIR := debian/fwupd-$(DEB_HOST_ARCH)-signed-template/usr/share/code-signing/fwupd-$(DEB_HOST_ARCH)-signed-template endif %: - dh $@ --with gir,systemd + [ -f debian/control ] || debian/rules regenerate_control + dh $@ --with gir -override_dh_auto_clean: +override_dh_auto_clean: regenerate_control rm -fr debian/build +ifeq (ubuntu,$(SB_STYLE)) + rm -rf debian/fwupd-images +endif override_dh_auto_configure: - if pkg-config --exists fwup; then \ - export UEFI="-Dplugin_uefi=true"; \ + if pkg-config --exists libsmbios_c; then \ + export DELL="-Dplugin_dell=true"; \ else \ - export UEFI="-Dplugin_uefi=false"; \ + export DELL="-Dplugin_dell=false"; \ fi; \ - if pkg-config --exists libsmbios_c; then \ - export DELL="-Dplugin_dell=true -Dplugin_synaptics=true"; \ + if pkg-config --exists efivar; then \ + export UEFI="-Dplugin_uefi=true -Dplugin_redfish=true -Dplugin_nvme=true"; \ + else \ + export UEFI="-Dplugin_uefi=false -Dplugin_redfish=false -Dplugin_nvme=false"; \ + fi; \ + if [ ! -z "$$CI" ]; then \ + export FLASHROM="-Dplugin_flashrom=true"; \ else \ - export DELL="-Dplugin_dell=false -Dplugin_synaptics=false"; \ + export FLASHROM="-Dplugin_flashrom=false"; \ fi; \ - dh_auto_configure -- $$UEFI $$DELL $$CI -Dplugin_dummy=true --libexecdir=/usr/lib + dh_auto_configure -- $$UEFI $$DELL $$FLASHROM $$CI -Dplugin_dummy=true --libexecdir=/usr/lib override_dh_install: find debian/tmp/usr -type f -name "*a" -print | xargs rm -f sed -i 's,wheel,sudo,' ./debian/tmp/usr/share/polkit-1/rules.d/org.freedesktop.fwupd.rules dh_install - dh_missing --fail-missing + #install the EFI binaries if needed + if [ -d debian/tmp/usr/lib/fwupd/efi/ ]; then \ + 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 [ ! -z "$$CI" ] && [ -f debian/tmp/usr/lib/xb-tool ]; then \ + dh_install -pfwupd usr/lib/xb-tool ;\ + fi + if [ ! -z "$$CI" ] && [ -f debian/tmp/usr/sbin/flashrom ]; then \ + dh_install -pfwupd usr/sbin/flashrom ;\ + 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 + mkdir -p $(TMPLDIR)/source-template/debian + cp -a debian/signing-template/* $(TMPLDIR)/source-template/debian + cp debian/README.Debian $(TMPLDIR)/source-template/debian + find $(TMPLDIR)/source-template/debian -type f | xargs sed -i "s,SIGNARCH,$(DEB_HOST_ARCH)," + find $(TMPLDIR)/source-template/debian -type f | xargs sed -i "s,SIGNVERSION,$(deb_version)," + for file in $$(find $(TMPLDIR)/source-template/debian -type f -name *SIGNARCH*); do file1=$$(echo $$file | sed "s,SIGNARCH,$(DEB_HOST_ARCH),"); mv -v $$file $$file1; done + install -m 0755 debian/fwupd.postinst $(TMPLDIR)/source-template/debian/fwupd-$(DEB_HOST_ARCH)-signed.postinst + install -m 0755 debian/fwupd.postrm $(TMPLDIR)/source-template/debian/fwupd-$(DEB_HOST_ARCH)-signed.postrm + ./debian/gen_signing_changelog $(TMPLDIR)/source-template/debian fwupd $(DEB_HOST_ARCH) + ./debian/gen_signing_json $(TMPLDIR) fwupd ${DEB_HOST_ARCH} +endif override_dh_strip_nondeterminism: dh_strip_nondeterminism -Xfirmware-example.xml.gz @@ -48,3 +97,18 @@ if [ -x /usr/bin/valgrind ] ; then \ dh_auto_test; \ fi + +override_dh_builddeb: + dh_builddeb +ifeq (ubuntu,$(SB_STYLE)) + if [ -d debian/tmp/usr/lib/fwupd/efi/ ]; then \ + mkdir -p debian/fwupd-images/$(deb_version) ;\ + cp debian/tmp/usr/lib/fwupd/efi/fwupd*.efi debian/fwupd-images/$(deb_version) ;\ + echo $(deb_version) > debian/fwupd-images/$(deb_version)/version ;\ + tar -C debian/fwupd-images -czvf ../$(tar_name) . ;\ + dpkg-distaddfile $(tar_name) raw-uefi - ;\ + fi +endif + +override_dh_shlibdeps: + dh_shlibdeps $$DHSLIBS diff -Nru fwupd-1.0.9/debian/signing-template/changelog.in fwupd-1.2.10/debian/signing-template/changelog.in --- fwupd-1.0.9/debian/signing-template/changelog.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/changelog.in 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,5 @@ +fwupd-SIGNARCH-signed (1) unstable; urgency=medium + + * Add template source package for signing + + -- Steve McIntyre <93sam@debian.org> Sat, 07 Apr 2018 12:44:55 +0100 diff -Nru fwupd-1.0.9/debian/signing-template/compat fwupd-1.2.10/debian/signing-template/compat --- fwupd-1.0.9/debian/signing-template/compat 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/compat 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1 @@ +9 diff -Nru fwupd-1.0.9/debian/signing-template/control fwupd-1.2.10/debian/signing-template/control --- fwupd-1.0.9/debian/signing-template/control 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/control 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,24 @@ +Source: fwupd-SIGNARCH-signed +Priority: optional +Maintainer: Debian EFI +Uploaders: Daniel Jared Dominguez , Steve McIntyre <93sam@debian.org>, Mario Limonciello +Build-Depends: debhelper (>= 9.0.0), sbsigntool [amd64 arm64 armhf i386], fwupd (= SIGNVERSION) [SIGNARCH] +Standards-Version: 4.1.3 +Section: libs +Homepage: https://github.com/hughsie/fwupd +Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git +Vcs-Browser: https://salsa.debian.org/efi-team/fwupd + +Package: fwupd-SIGNARCH-signed +Section: admin +Architecture: SIGNARCH +Provides: fwupd-signed +Depends: ${shlibs:Depends}, ${misc:Depends}, fwupd (= SIGNVERSION) +Description: Tools to manage UEFI firmware updates (signed) + fwupd provides functionality to update system firmware. It has been + initially designed to update firmware using UEFI capsule updates, but + it is designed to be extensible to other firmware update standards. + . + This package contains just the signed version of the fwupd binary, + needed if your system has UEFI Secure Boot enabled. It depends on the + normal fwupd package for everything else. diff -Nru fwupd-1.0.9/debian/signing-template/copyright fwupd-1.2.10/debian/signing-template/copyright --- fwupd-1.0.9/debian/signing-template/copyright 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/copyright 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,33 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: fwupd +Source: https://github.com/hughsie/fwupd + +Files: * +Copyright: 2015 Richard Hughes +License: LGPL-2.1+ + +Files: data/tests/colorhug/firmware.metainfo.xml +Copyright: 2015 Richard Hughes +License: CC0-1.0 + +Files: debian/* +Copyright: 2015 Daniel Jared Dominguez + 2015 Mario Limonciello +License: LGPL-2.1+ + +License: LGPL-2.1+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 2.1 can be found in "/usr/share/common-licenses/LGPL-2.1". diff -Nru fwupd-1.0.9/debian/signing-template/fwupd-SIGNARCH-signed.install fwupd-1.2.10/debian/signing-template/fwupd-SIGNARCH-signed.install --- fwupd-1.0.9/debian/signing-template/fwupd-SIGNARCH-signed.install 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/fwupd-SIGNARCH-signed.install 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1 @@ +*.efi.signed /usr/lib/fwupd/efi diff -Nru fwupd-1.0.9/debian/signing-template/README.source fwupd-1.2.10/debian/signing-template/README.source --- fwupd-1.0.9/debian/signing-template/README.source 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/README.source 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,4 @@ +This source package is generated by the Debian signing service from a +template built by the fwupd package. It should never be updated directly. + + -- Steve McIntyre <93sam@debian.org> Sat, 07 Apr 2018 12:44:55 +0100 diff -Nru fwupd-1.0.9/debian/signing-template/rules fwupd-1.2.10/debian/signing-template/rules --- fwupd-1.0.9/debian/signing-template/rules 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/rules 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1,15 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +PACKAGE_NAME := fwupd +SIG_PKG_NAME := fwupd-SIGNARCH-signed +SIGNATURE_DIR := debian/signatures/$(PACKAGE_NAME) +BINARY := $(shell find /usr/lib/fwupd/efi -name '*.efi' | xargs basename) + +%: + dh $@ + +override_dh_auto_build: + cp /usr/lib/fwupd/efi/$(BINARY) . + sbattach --attach $(SIGNATURE_DIR)/usr/lib/fwupd/efi/$(BINARY).sig $(BINARY) + mv $(BINARY) $(BINARY).signed diff -Nru fwupd-1.0.9/debian/signing-template/source/format fwupd-1.2.10/debian/signing-template/source/format --- fwupd-1.0.9/debian/signing-template/source/format 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/debian/signing-template/source/format 2019-12-19 00:13:48.000000000 +0000 @@ -0,0 +1 @@ +3.0 (native) diff -Nru fwupd-1.0.9/debian/source/lintian-overrides fwupd-1.2.10/debian/source/lintian-overrides --- fwupd-1.0.9/debian/source/lintian-overrides 2018-09-11 18:33:14.000000000 +0000 +++ fwupd-1.2.10/debian/source/lintian-overrides 2019-12-19 00:13:48.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/docs/architecture-plan.svg fwupd-1.2.10/docs/architecture-plan.svg --- fwupd-1.0.9/docs/architecture-plan.svg 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/docs/architecture-plan.svg 2019-07-15 18:25:54.000000000 +0000 @@ -13,7 +13,7 @@ height="744.09448" id="svg2" version="1.1" - inkscape:version="0.91 r13725" + inkscape:version="0.92.3 (2405546, 2018-03-11)" sodipodi:docname="architecture-plan.svg" inkscape:export-filename="/home/hughsie/Code/colord/doc/website/img/architecture-plan.png" inkscape:export-xdpi="59.99197" @@ -184,6 +184,34 @@ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + + + + + + + ry="5" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> fwupd + x="540.91553" + y="710.87421" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:1.25;font-family:Cantarell;-inkscape-font-specification:Cantarell;text-align:center;text-anchor:middle">fwupd + style="fill:#fce94f;fill-opacity:1;stroke:#edd400;stroke-width:1.88976378;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> ESRT + style="fill:#fce94f;fill-opacity:1;stroke:#edd400;stroke-width:1.88976378;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> VendorcustomProvders + id="tspan1140">plugins + ry="5" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> Udev + x="269.30994" + y="810.13818" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:1.25;font-family:Cantarell;-inkscape-font-specification:Cantarell;text-align:center;text-anchor:middle">udev + style="fill:#ad7fa8;fill-opacity:1;stroke:#75507b;stroke-width:1.88976378;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> systemd + ry="5" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> pending.db + x="809.0918" + y="807.82349" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:1.25;font-family:Cantarell;-inkscape-font-specification:Cantarell;text-align:center;text-anchor:middle">pending.db + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" + sodipodi:nodetypes="cc" /> + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" + sodipodi:nodetypes="ccc" /> + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" + sodipodi:nodetypes="cc" /> + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> session + x="48.273308" + y="481.51367" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:1.25;font-family:Cantarell;-inkscape-font-specification:Cantarell">internet system - system + - - - fwupdmgr + style="fill:#729fcf;fill-opacity:1;stroke:#3465a4;stroke-width:1.88976383;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> + style="fill:#d3d7cf;fill-opacity:1;stroke:#888a85;stroke-width:1.88976383;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> downloadcache - + y="560.53821" + x="809.99036" + style="font-style:normal;font-weight:normal;line-height:0%;font-family:'Bitstream Vera Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" + xml:space="preserve" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408">downloadcache - + style="fill:#8ae234;fill-opacity:1;stroke:#73d216;stroke-width:1.88976383;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> Internet + sodipodi:role="line">CDN - Gudev + rules + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" + sodipodi:nodetypes="cc" /> sqlite $home - gnome-softwaregnome-software + id="tspan1643">fwupdmgr + + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" + sodipodi:nodetypes="cc" /> UpdateMetadata() GetDevices() + height="60" + x="720" + y="872.36218" + ry="5" /> sysfs + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" + sodipodi:nodetypes="ccc" /> metadata + sodipodi:role="line">only metadata firmware - firmware + + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> + inkscape:export-filename="/home/hughsie/Documents/Presentations/LVFS/010-architecture-plan.png" + inkscape:export-xdpi="119.94408" + inkscape:export-ydpi="119.94408" /> AppStream XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LVFS + + + + + session + embargoed metadata + + + diff -Nru fwupd-1.0.9/docs/libfwupd/libfwupd-docs.xml fwupd-1.2.10/docs/libfwupd/libfwupd-docs.xml --- fwupd-1.0.9/docs/libfwupd/libfwupd-docs.xml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/docs/libfwupd/libfwupd-docs.xml 2019-07-15 18:25:54.000000000 +0000 @@ -41,13 +41,15 @@ Functionality available to plugins. - - - - + - + + + + + + @@ -153,7 +155,7 @@
- Creating an abstact device + Creating an abstract device This section shows how you would create a device which is exported to the daemon and thus can be queried and updated by the client software. @@ -292,7 +294,7 @@ For some hardware, we might want to do an action before or after the actual firmware is squirted into the device. This could be something as simple as checking the system battery - level is over a certain theshold, or it could be as complicated as + level is over a certain threshold, or it could be as complicated as ensuring a vendor-specific GPIO is asserted when specific types of hardware are updated. @@ -332,7 +334,7 @@ Detaching to bootloader mode Some hardware can only be updated in a special bootloader mode, which - for most devices can be switched to automaticaly. + for most devices can be switched to automatically. In some cases the user to do something manually, for instance re-inserting the hardware with a secret button pressed. diff -Nru fwupd-1.0.9/docs/meson.build fwupd-1.2.10/docs/meson.build --- fwupd-1.0.9/docs/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/docs/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,3 +1 @@ -if get_option('gtkdoc') - subdir('libfwupd') -endif +subdir('libfwupd') diff -Nru fwupd-1.0.9/docs/version-format.md fwupd-1.2.10/docs/version-format.md --- fwupd-1.0.9/docs/version-format.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/docs/version-format.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/.github/pull_request_template.md fwupd-1.2.10/.github/pull_request_template.md --- fwupd-1.0.9/.github/pull_request_template.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/.github/pull_request_template.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/.gitignore fwupd-1.2.10/.gitignore --- fwupd-1.0.9/.gitignore 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/.gitignore 2019-07-15 18:25:54.000000000 +0000 @@ -11,3 +11,13 @@ /prime /stage /snap/.snapcraft +/libxmlb +/*.deb +/*.ddeb +/*.changes +/*.buildinfo +/fwupd*.build +/*.dsc +/*.xz +/*.gz +__pycache__ diff -Nru fwupd-1.0.9/.gitmodules fwupd-1.2.10/.gitmodules --- fwupd-1.0.9/.gitmodules 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/.gitmodules 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,3 @@ +[submodule "contrib/flatpak"] + path = contrib/flatpak + url = https://github.com/flathub/org.freedesktop.fwupd diff -Nru fwupd-1.0.9/.lgtm.yml fwupd-1.2.10/.lgtm.yml --- fwupd-1.0.9/.lgtm.yml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/.lgtm.yml 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,23 @@ +extraction: + python: + python_setup: + version: "3" + cpp: + prepare: + packages: + - bsdtar + - python3-gi + - libcogl-pango-dev + - python3-pil + - python3-cairo + after_prepare: + - "wget -O libxmlb.zip https://github.com/hughsie/libxmlb/archive/0.1.7.zip" + - "mkdir -p subprojects/libxmlb" + - "bsdtar --strip-components=1 -xvf libxmlb.zip -C subprojects/libxmlb" + - "wget -O flashrom.zip https://github.com/hughsie/flashrom/archive/wip/hughsie/fwupd.zip" + - "mkdir -p subprojects/flashrom" + - "bsdtar --strip-components=1 -xvf flashrom.zip -C subprojects/flashrom" + index: + build_command: + - "meson setup build" + - "ninja -C build" diff -Nru fwupd-1.0.9/libfwupd/fwupd-client.c fwupd-1.2.10/libfwupd/fwupd-client.c --- fwupd-1.0.9/libfwupd/fwupd-client.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-client.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -38,6 +37,7 @@ typedef struct { FwupdStatus status; + gboolean tainted; guint percentage; gchar *daemon_version; GDBusConnection *conn; @@ -58,6 +58,7 @@ PROP_STATUS, PROP_PERCENTAGE, PROP_DAEMON_VERSION, + PROP_TAINTED, PROP_LAST }; @@ -132,6 +133,14 @@ 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"); @@ -204,6 +213,7 @@ { 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); @@ -236,91 +246,12 @@ 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; } -static GPtrArray * -fwupd_client_parse_releases_from_variant (GVariant *val) -{ - GPtrArray *array = NULL; - gsize sz; - g_autoptr(GVariant) untuple = NULL; - - array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - untuple = g_variant_get_child_value (val, 0); - sz = g_variant_n_children (untuple); - for (guint i = 0; i < sz; i++) { - FwupdRelease *rel; - g_autoptr(GVariant) data = NULL; - data = g_variant_get_child_value (untuple, i); - rel = fwupd_release_from_variant (data); - if (rel == NULL) - continue; - g_ptr_array_add (array, rel); - } - return array; -} - -static GPtrArray * -fwupd_client_parse_devices_from_variant (GVariant *val) -{ - GPtrArray *array = NULL; - gsize sz; - g_autoptr(GVariant) untuple = NULL; - g_autoptr(GHashTable) devices_by_id = NULL; - - array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - devices_by_id = g_hash_table_new (g_str_hash, g_str_equal); - untuple = g_variant_get_child_value (val, 0); - sz = g_variant_n_children (untuple); - for (guint i = 0; i < sz; i++) { - FwupdDevice *dev; - g_autoptr(GVariant) data = NULL; - data = g_variant_get_child_value (untuple, i); - dev = fwupd_device_from_variant (data); - if (dev == NULL) - continue; - g_ptr_array_add (array, dev); - if (fwupd_device_get_id (dev) != NULL) { - g_hash_table_insert (devices_by_id, - (gpointer) fwupd_device_get_id (dev), - (gpointer) dev); - } - } - - /* set the parent on each child */ - for (guint i = 0; i < array->len; i++) { - FwupdDevice *dev = g_ptr_array_index (array, i); - const gchar *parent_id = fwupd_device_get_parent_id (dev); - if (parent_id != NULL) { - FwupdDevice *dev_tmp; - dev_tmp = g_hash_table_lookup (devices_by_id, parent_id); - fwupd_device_set_parent (dev, dev_tmp); - } - } - - return array; -} - -static GPtrArray * -fwupd_client_parse_remotes_from_data (GVariant *devices) -{ - GPtrArray *remotes = NULL; - gsize sz; - g_autoptr(GVariant) untuple = NULL; - - remotes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - untuple = g_variant_get_child_value (devices, 0); - sz = g_variant_n_children (untuple); - for (guint i = 0; i < sz; i++) { - g_autoptr(GVariant) data = g_variant_get_child_value (untuple, i); - FwupdRemote *remote = fwupd_remote_from_variant (data); - g_ptr_array_add (remotes, remote); - } - - return remotes; -} - static void fwupd_client_fixup_dbus_error (GError *error) { @@ -393,7 +324,7 @@ fwupd_client_fixup_dbus_error (*error); return NULL; } - return fwupd_client_parse_devices_from_variant (val); + return fwupd_device_array_from_variant (val); } /** @@ -435,7 +366,7 @@ fwupd_client_fixup_dbus_error (*error); return NULL; } - return fwupd_client_parse_devices_from_variant (val); + return fwupd_device_array_from_variant (val); } /** @@ -524,7 +455,7 @@ fwupd_client_fixup_dbus_error (*error); return NULL; } - return fwupd_client_parse_releases_from_variant (val); + return fwupd_release_array_from_variant (val); } /** @@ -569,7 +500,7 @@ fwupd_client_fixup_dbus_error (*error); return NULL; } - return fwupd_client_parse_releases_from_variant (val); + return fwupd_release_array_from_variant (val); } /** @@ -614,7 +545,7 @@ fwupd_client_fixup_dbus_error (*error); return NULL; } - return fwupd_client_parse_releases_from_variant (val); + return fwupd_release_array_from_variant (val); } static void @@ -631,6 +562,104 @@ } /** + * fwupd_client_modify_config + * @client: A #FwupdClient + * @key: key, e.g. `BlacklistPlugins` + * @value: value, e.g. `*` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Modifies a daemon config option. + * The daemon will only respond to this request with proper permissions + * + * Returns: %TRUE for success + * + * Since: 1.2.8 + **/ +gboolean +fwupd_client_modify_config (FwupdClient *client, const gchar *key, const gchar *value, + GCancellable *cancellable, GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (client, cancellable, error)) + return FALSE; + + /* call into daemon */ + helper = fwupd_client_helper_new (); + g_dbus_proxy_call (priv->proxy, + "ModifyConfig", + g_variant_new ("(ss)", key, value), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + fwupd_client_proxy_call_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, helper->error); + helper->error = NULL; + return FALSE; + } + return TRUE; +} + +/** + * fwupd_client_activate: + * @client: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @device_id: a device + * @error: the #GError, or %NULL + * + * Activates up a device, which normally means the device switches to a new + * firmware version. This should only be called when data loss cannot occur. + * + * Returns: %TRUE for success + * + * Since: 1.2.6 + **/ +gboolean +fwupd_client_activate (FwupdClient *client, GCancellable *cancellable, + const gchar *device_id, GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (client, cancellable, error)) + return FALSE; + + /* call into daemon */ + helper = fwupd_client_helper_new (); + g_dbus_proxy_call (priv->proxy, + "Activate", + g_variant_new ("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + fwupd_client_proxy_call_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, helper->error); + helper->error = NULL; + return FALSE; + } + return TRUE; +} + +/** * fwupd_client_verify: * @client: A #FwupdClient * @device_id: the device ID @@ -932,7 +961,7 @@ return FALSE; /* set options */ - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&builder, "{sv}", "reason", g_variant_new_string ("user-action")); g_variant_builder_add (&builder, "{sv}", @@ -1082,7 +1111,7 @@ } /* return results */ - return fwupd_client_parse_devices_from_variant (helper->val); + return fwupd_device_array_from_variant (helper->val); } /** @@ -1140,6 +1169,24 @@ } /** + * 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 * @remote_id: the remote ID, e.g. `lvfs-testing` @@ -1281,7 +1328,161 @@ fwupd_client_fixup_dbus_error (*error); return NULL; } - return fwupd_client_parse_remotes_from_data (val); + return fwupd_remote_array_from_variant (val); +} + +/** + * fwupd_client_get_approved_firmware: + * @client: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets the list of approved firmware. + * + * Returns: (transfer full): list of remotes, or %NULL + * + * Since: 1.2.6 + **/ +gchar ** +fwupd_client_get_approved_firmware (FwupdClient *client, + GCancellable *cancellable, + GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_autoptr(GVariant) val = NULL; + gchar **retval = NULL; + + g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (client, cancellable, error)) + return NULL; + + /* call into daemon */ + val = g_dbus_proxy_call_sync (priv->proxy, + "GetApprovedFirmware", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (val == NULL) { + if (error != NULL) + fwupd_client_fixup_dbus_error (*error); + return NULL; + } + g_variant_get (val, "(^as)", &retval); + return retval; +} + +/** + * fwupd_client_set_approved_firmware: + * @client: A #FwupdClient + * @checksums: Array of checksums + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Sets the list of approved firmware. + * + * Returns: %TRUE for success + * + * Since: 1.2.6 + **/ +gboolean +fwupd_client_set_approved_firmware (FwupdClient *client, + gchar **checksums, + GCancellable *cancellable, + GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (client, cancellable, error)) + return FALSE; + + /* call into daemon */ + val = g_dbus_proxy_call_sync (priv->proxy, + "SetApprovedFirmware", + g_variant_new ("(^as)", checksums), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (val == NULL) { + if (error != NULL) + fwupd_client_fixup_dbus_error (*error); + return FALSE; + } + return TRUE; +} + +/** + * fwupd_client_self_sign: + * @client: A #FwupdClient + * @value: A string to sign, typically a JSON blob + * @flags: #FwupdSelfSignFlags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Signs the data using the client self-signed certificate. + * + * Returns: %TRUE for success + * + * Since: 1.2.6 + **/ +gchar * +fwupd_client_self_sign (FwupdClient *client, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + GVariantBuilder builder; + g_autoptr(GVariant) val = NULL; + gchar *retval = NULL; + + g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (client, cancellable, error)) + return NULL; + + /* set options */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + if (flags & FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP) { + g_variant_builder_add (&builder, "{sv}", + "add-timestamp", g_variant_new_boolean (TRUE)); + } + if (flags & FWUPD_SELF_SIGN_FLAG_ADD_CERT) { + g_variant_builder_add (&builder, "{sv}", + "add-cert", g_variant_new_boolean (TRUE)); + } + + /* call into daemon */ + val = g_dbus_proxy_call_sync (priv->proxy, + "SelfSign", + g_variant_new ("(sa{sv})", value, &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (val == NULL) { + if (error != NULL) + fwupd_client_fixup_dbus_error (*error); + return NULL; + } + g_variant_get (val, "(s)", &retval); + return retval; } /** @@ -1462,6 +1663,9 @@ 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; @@ -1596,10 +1800,21 @@ */ 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: * * The last-reported percentage of the daemon. @@ -1608,7 +1823,7 @@ */ 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); /** @@ -1619,7 +1834,7 @@ * 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 -Nru fwupd-1.0.9/libfwupd/fwupd-client.h fwupd-1.2.10/libfwupd/fwupd-client.h --- fwupd-1.0.9/libfwupd/fwupd-client.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-client.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_CLIENT_H -#define __FWUPD_CLIENT_H +#pragma once #include #include @@ -80,6 +78,15 @@ const gchar *device_id, GCancellable *cancellable, GError **error); +gboolean fwupd_client_modify_config (FwupdClient *client, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_activate (FwupdClient *client, + GCancellable *cancellable, + const gchar *device_id, + GError **error); gboolean fwupd_client_clear_results (FwupdClient *client, const gchar *device_id, GCancellable *cancellable, @@ -117,6 +124,7 @@ 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); @@ -128,7 +136,17 @@ GCancellable *cancellable, GError **error); -G_END_DECLS - -#endif /* __FWUPD_CLIENT_H */ +gchar **fwupd_client_get_approved_firmware (FwupdClient *client, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_set_approved_firmware (FwupdClient *client, + gchar **checksums, + GCancellable *cancellable, + GError **error); +gchar *fwupd_client_self_sign (FwupdClient *client, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GError **error); +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-common.c fwupd-1.2.10/libfwupd/fwupd-common.c --- fwupd-1.0.9/libfwupd/fwupd-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -17,6 +16,10 @@ #include #include +#if !GLIB_CHECK_VERSION(2,54,0) +#include +#endif + /** * fwupd_checksum_guess_kind: * @checksum: A checksum @@ -46,7 +49,7 @@ } 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"; @@ -73,7 +76,9 @@ 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); } /** @@ -357,6 +362,24 @@ 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); + if (checksums->len > 0) { + json_builder_set_member_name (builder, "ChecksumDevice"); + json_builder_begin_array (builder); + for (guint i = 0; i < checksums->len; i++) { + const gchar *checksum = g_ptr_array_index (checksums, i); + json_builder_add_string_value (builder, checksum); + } + json_builder_end_array (builder); + } + + /* 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)); @@ -364,6 +387,10 @@ 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"); @@ -492,3 +519,305 @@ } return data; } + +#define FWUPD_GUID_NAMESPACE_DEFAULT "6ba7b810-9dad-11d1-80b4-00c04fd430c8" +#define FWUPD_GUID_NAMESPACE_MICROSOFT "70ffd812-4c7f-4c7d-0000-000000000000" + +typedef struct __attribute__((packed)) { + guint32 a; + guint16 b; + guint16 c; + guint16 d; + guint8 e[6]; +} fwupd_guid_native_t; + +/** + * fwupd_guid_to_string: + * @guid: a #fwupd_guid_t to read + * @flags: some %FwupdGuidFlags, e.g. %FWUPD_GUID_FLAG_MIXED_ENDIAN + * + * Returns a text GUID of mixed or BE endian for a packed buffer. + * + * Returns: A new GUID + * + * Since: 1.2.5 + **/ +gchar * +fwupd_guid_to_string (const fwupd_guid_t *guid, FwupdGuidFlags flags) +{ + fwupd_guid_native_t gnat; + + g_return_val_if_fail (guid != NULL, NULL); + + /* copy to avoid issues with aligning */ + memcpy (&gnat, guid, sizeof(gnat)); + + /* mixed is bizaar, but specified as the DCE encoding */ + if (flags & FWUPD_GUID_FLAG_MIXED_ENDIAN) { + return g_strdup_printf ("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + GUINT32_FROM_LE(gnat.a), + GUINT16_FROM_LE(gnat.b), + GUINT16_FROM_LE(gnat.c), + GUINT16_FROM_BE(gnat.d), + gnat.e[0], gnat.e[1], + gnat.e[2], gnat.e[3], + gnat.e[4], gnat.e[5]); + } + return g_strdup_printf ("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + GUINT32_FROM_BE(gnat.a), + GUINT16_FROM_BE(gnat.b), + GUINT16_FROM_BE(gnat.c), + GUINT16_FROM_BE(gnat.d), + gnat.e[0], gnat.e[1], + gnat.e[2], gnat.e[3], + gnat.e[4], gnat.e[5]); +} + +#if !GLIB_CHECK_VERSION(2,54,0) +static gboolean +str_has_sign (const gchar *str) +{ + return str[0] == '-' || str[0] == '+'; +} + +static gboolean +str_has_hex_prefix (const gchar *str) +{ + return str[0] == '0' && g_ascii_tolower (str[1]) == 'x'; +} + +static gboolean +g_ascii_string_to_unsigned (const gchar *str, + guint base, + guint64 min, + guint64 max, + guint64 *out_num, + GError **error) +{ + const gchar *end_ptr = NULL; + gint saved_errno = 0; + guint64 number; + + g_return_val_if_fail (str != NULL, FALSE); + g_return_val_if_fail (base >= 2 && base <= 36, FALSE); + g_return_val_if_fail (min <= max, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (str[0] == '\0') { + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "Empty string is not a number"); + return FALSE; + } + + errno = 0; + number = g_ascii_strtoull (str, (gchar **)&end_ptr, base); + saved_errno = errno; + + if (g_ascii_isspace (str[0]) || str_has_sign (str) || + (base == 16 && str_has_hex_prefix (str)) || + (saved_errno != 0 && saved_errno != ERANGE) || + end_ptr == NULL || + *end_ptr != '\0') { + g_set_error (error, + G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "“%s” is not an unsigned number", str); + return FALSE; + } + if (saved_errno == ERANGE || number < min || number > max) { + g_autofree gchar *min_str = g_strdup_printf ("%" G_GUINT64_FORMAT, min); + g_autofree gchar *max_str = g_strdup_printf ("%" G_GUINT64_FORMAT, max); + g_set_error (error, + G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "Number “%s” is out of bounds [%s, %s]", + str, min_str, max_str); + return FALSE; + } + if (out_num != NULL) + *out_num = number; + return TRUE; +} +#endif /* GLIB_CHECK_VERSION(2,54,0) */ + +/** + * fwupd_guid_from_string: + * @guidstr: (nullable): a GUID, e.g. `00112233-4455-6677-8899-aabbccddeeff` + * @guid: a #fwupd_guid_t, or NULL to just check the GUID + * @flags: some %FwupdGuidFlags, e.g. %FWUPD_GUID_FLAG_MIXED_ENDIAN + * @error: A #GError or %NULL + * + * Converts a string GUID into its binary encoding. All string GUIDs are + * formatted as big endian but on-disk can be encoded in different ways. + * + * Returns: %TRUE for success + * + * Since: 1.2.5 + **/ +gboolean +fwupd_guid_from_string (const gchar *guidstr, + fwupd_guid_t *guid, + FwupdGuidFlags flags, + GError **error) +{ + fwupd_guid_native_t gu = { 0x0 }; + gboolean mixed_endian = flags & FWUPD_GUID_FLAG_MIXED_ENDIAN; + guint64 tmp; + g_auto(GStrv) split = NULL; + + g_return_val_if_fail (guidstr != NULL, FALSE); + + /* split into sections */ + if (strlen (guidstr) != 36) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "is not valid format"); + return FALSE; + } + split = g_strsplit (guidstr, "-", 5); + if (g_strv_length (split) != 5) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "is not valid format, no dashes"); + return FALSE; + } + if (strlen (split[0]) != 8 && strlen (split[1]) != 4 && + strlen (split[2]) != 4 && strlen (split[3]) != 4 && + strlen (split[4]) != 12) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "is not valid format, not GUID"); + return FALSE; + } + + /* parse */ + if (!g_ascii_string_to_unsigned (split[0], 16, 0, 0xffffffff, &tmp, error)) + return FALSE; + gu.a = mixed_endian ? GUINT32_TO_LE(tmp) : GUINT32_TO_BE(tmp); + if (!g_ascii_string_to_unsigned (split[1], 16, 0, 0xffff, &tmp, error)) + return FALSE; + gu.b = mixed_endian ? GUINT16_TO_LE(tmp) : GUINT16_TO_BE(tmp); + if (!g_ascii_string_to_unsigned (split[2], 16, 0, 0xffff, &tmp, error)) + return FALSE; + gu.c = mixed_endian ? GUINT16_TO_LE(tmp) : GUINT16_TO_BE(tmp); + if (!g_ascii_string_to_unsigned (split[3], 16, 0, 0xffff, &tmp, error)) + return FALSE; + gu.d = GUINT16_TO_BE(tmp); + for (guint i = 0; i < 6; i++) { + gchar buffer[3] = { 0x0 }; + memcpy (buffer, split[4] + (i * 2), 2); + if (!g_ascii_string_to_unsigned (buffer, 16, 0, 0xff, &tmp, error)) + return FALSE; + gu.e[i] = tmp; + } + if (guid != NULL) + memcpy (guid, &gu, sizeof(gu)); + + /* success */ + return TRUE; +} + +/** + * fwupd_guid_hash_data: + * @data: data to hash + * @datasz: length of @data + * @flags: some %FwupdGuidFlags, e.g. %FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT + * + * 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 for internal error + * + * Since: 1.2.5 + **/ +gchar * +fwupd_guid_hash_data (const guint8 *data, gsize datasz, FwupdGuidFlags flags) +{ + const gchar *namespace_id = FWUPD_GUID_NAMESPACE_DEFAULT; + gsize digestlen = 20; + guint8 hash[20]; + fwupd_guid_t uu_namespace; + fwupd_guid_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 (datasz != 0, NULL); + + /* old MS GUID */ + if (flags & FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT) + namespace_id = FWUPD_GUID_NAMESPACE_MICROSOFT; + + /* convert the namespace to binary: hardcoded BE, not @flags */ + if (!fwupd_guid_from_string (namespace_id, &uu_namespace, FWUPD_GUID_FLAG_NONE, NULL)) + return NULL; + + /* hash the namespace and then the string */ + csum = g_checksum_new (G_CHECKSUM_SHA1); + g_checksum_update (csum, (guchar *) &uu_namespace, sizeof(uu_namespace)); + g_checksum_update (csum, (guchar *) data, (gssize) datasz); + g_checksum_get_digest (csum, hash, &digestlen); + + /* copy most parts of the hash 1:1 */ + memcpy (uu_new, hash, sizeof(uu_new)); + + /* 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 fwupd_guid_to_string ((const fwupd_guid_t *) &uu_new, flags); +} + +/** + * fwupd_guid_is_valid: + * @guid: string to check, e.g. `00112233-4455-6677-8899-aabbccddeeff` + * + * Checks the string is a valid GUID. + * + * Returns: %TRUE if @guid was a valid GUID, %FALSE otherwise + * + * Since: 1.2.5 + **/ +gboolean +fwupd_guid_is_valid (const gchar *guid) +{ + if (guid == NULL) + return FALSE; + if (!fwupd_guid_from_string (guid, NULL, FWUPD_GUID_FLAG_NONE, NULL)) + return FALSE; + if (g_strcmp0 (guid, "00000000-0000-0000-0000-000000000000") == 0) + return FALSE; + return TRUE; +} + +/** + * fwupd_guid_hash_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 default 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.5 + **/ +gchar * +fwupd_guid_hash_string (const gchar *str) +{ + if (str == NULL || str[0] == '\0') + return NULL; + return fwupd_guid_hash_data ((const guint8 *) str, strlen (str), + FWUPD_GUID_FLAG_NONE); +} diff -Nru fwupd-1.0.9/libfwupd/fwupd-common.h fwupd-1.2.10/libfwupd/fwupd-common.h --- fwupd-1.0.9/libfwupd/fwupd-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,21 +1,42 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_COMMON_H -#define __FWUPD_COMMON_H +#pragma once #include +G_BEGIN_DECLS + #define FWUPD_DBUS_PATH "/" #define FWUPD_DBUS_SERVICE "org.freedesktop.fwupd" #define FWUPD_DBUS_INTERFACE "org.freedesktop.fwupd" #define FWUPD_DEVICE_ID_ANY "*" +/** + * FwupdGuidFlags: + * @FWUPD_GUID_FLAG_NONE: No trust + * @FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT: Use the Microsoft-compatible namespace + * @FWUPD_GUID_FLAG_MIXED_ENDIAN: Use EFI mixed endian representation + * + * The flags to show how the data should be converted. + **/ +typedef enum { + FWUPD_GUID_FLAG_NONE = 0, /* Since: 1.2.5 */ + FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT = 1 << 0, /* Since: 1.2.5 */ + FWUPD_GUID_FLAG_MIXED_ENDIAN = 1 << 1, /* Since: 1.2.5 */ + /*< private >*/ + FWUPD_GUID_FLAG_LAST +} FwupdGuidFlags; + +/* GObject Introspection does not understand typedefs with sizes */ +#ifndef __GI_SCANNER__ +typedef guint8 fwupd_guid_t[16]; +#endif + const gchar *fwupd_checksum_get_best (GPtrArray *checksums); const gchar *fwupd_checksum_get_by_kind (GPtrArray *checksums, GChecksumType kind); @@ -27,5 +48,25 @@ GHashTable *fwupd_get_os_release (GError **error); gchar *fwupd_build_history_report_json (GPtrArray *devices, GError **error); +#ifndef __GI_SCANNER__ +gchar *fwupd_guid_to_string (const fwupd_guid_t *guid, + FwupdGuidFlags flags); +gboolean fwupd_guid_from_string (const gchar *guidstr, + fwupd_guid_t *guid, + FwupdGuidFlags flags, + GError **error); +#else +gchar *fwupd_guid_to_string (const guint8 guid[16], + FwupdGuidFlags flags); +gboolean fwupd_guid_from_string (const gchar *guidstr, + guint8 guid[16], + FwupdGuidFlags flags, + GError **error); +#endif +gboolean fwupd_guid_is_valid (const gchar *guid); +gchar *fwupd_guid_hash_string (const gchar *str); +gchar *fwupd_guid_hash_data (const guint8 *data, + gsize datasz, + FwupdGuidFlags flags); -#endif /* __FWUPD_COMMON_H */ +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-common-private.h fwupd-1.2.10/libfwupd/fwupd-common-private.h --- fwupd-1.0.9/libfwupd/fwupd-common-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-common-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,17 +1,17 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_COMMON_PRIVATE_H -#define __FWUPD_COMMON_PRIVATE_H +#pragma once #include #include "fwupd-common.h" +G_BEGIN_DECLS + gchar *fwupd_checksum_format_for_display (const gchar *checksum); -#endif /* __FWUPD_COMMON_PRIVATE_H */ +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-deprecated.h fwupd-1.2.10/libfwupd/fwupd-deprecated.h --- fwupd-1.0.9/libfwupd/fwupd-deprecated.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-deprecated.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,13 +1,13 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_DEPRECATED_H -#define __FWUPD_DEPRECATED_H +#pragma once + +G_BEGIN_DECLS /* indeed, nothing */ -#endif /* __FWUPD_DEPRECATED_H */ +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-device.c fwupd-1.2.10/libfwupd/fwupd-device.c --- fwupd-1.0.9/libfwupd/fwupd-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -35,8 +34,10 @@ guint64 modified; guint64 flags; GPtrArray *guids; + GPtrArray *instance_ids; GPtrArray *icons; gchar *name; + gchar *serial; gchar *summary; gchar *description; gchar *vendor; @@ -46,14 +47,23 @@ gchar *version; gchar *version_lowest; gchar *version_bootloader; + FwupdVersionFormat version_format; GPtrArray *checksums; guint32 flashes_left; + guint32 install_duration; FwupdUpdateState update_state; gchar *update_error; + gchar *update_message; GPtrArray *releases; FwupdDevice *parent; } FwupdDevicePrivate; +enum { + PROP_0, + PROP_VERSION_FORMAT, + PROP_LAST +}; + G_DEFINE_TYPE_WITH_PRIVATE (FwupdDevice, fwupd_device, G_TYPE_OBJECT) #define GET_PRIVATE(o) (fwupd_device_get_instance_private (o)) @@ -135,6 +145,42 @@ } /** + * fwupd_device_get_serial: + * @device: A #FwupdDevice + * + * Gets the serial number for the device. + * + * Returns: a string value, or %NULL if never set. + * + * Since: 1.1.2 + **/ +const gchar * +fwupd_device_get_serial (FwupdDevice *device) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); + return priv->serial; +} + +/** + * fwupd_device_set_serial: + * @device: A #FwupdDevice + * @serial: the device serial number + * + * Sets the serial number for the device. + * + * Since: 1.1.2 + **/ +void +fwupd_device_set_serial (FwupdDevice *device, const gchar *serial) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FWUPD_IS_DEVICE (device)); + g_free (priv->serial); + priv->serial = g_strdup (serial); +} + +/** * fwupd_device_get_id: * @device: A #FwupdDevice * @@ -325,6 +371,69 @@ } /** + * fwupd_device_get_instance_ids: + * @device: A #FwupdDevice + * + * Gets the InstanceIDs. + * + * Returns: (element-type utf8) (transfer none): the InstanceID + * + * Since: 1.2.5 + **/ +GPtrArray * +fwupd_device_get_instance_ids (FwupdDevice *device) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); + return priv->instance_ids; +} + +/** + * fwupd_device_has_instance_id: + * @device: A #FwupdDevice + * @instance_id: the InstanceID, e.g. `PCI\VEN_10EC&DEV_525A` + * + * Finds out if the device has this specific InstanceID. + * + * Returns: %TRUE if the InstanceID is found + * + * Since: 1.2.5 + **/ +gboolean +fwupd_device_has_instance_id (FwupdDevice *device, const gchar *instance_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + + g_return_val_if_fail (FWUPD_IS_DEVICE (device), FALSE); + + for (guint i = 0; i < priv->instance_ids->len; i++) { + const gchar *instance_id_tmp = g_ptr_array_index (priv->instance_ids, i); + if (g_strcmp0 (instance_id, instance_id_tmp) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_device_add_instance_id: + * @device: A #FwupdDevice + * @instance_id: the GUID, e.g. `PCI\VEN_10EC&DEV_525A` + * + * Adds the InstanceID if it does not already exist. + * + * Since: 1.2.5 + **/ +void +fwupd_device_add_instance_id (FwupdDevice *device, const gchar *instance_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FWUPD_IS_DEVICE (device)); + if (fwupd_device_has_instance_id (device, instance_id)) + return; + g_ptr_array_add (priv->instance_ids, g_strdup (instance_id)); +} + +/** * fwupd_device_get_icons: * @device: A #FwupdDevice * @@ -664,6 +773,41 @@ } /** + * fwupd_device_get_install_duration: + * @device: A #FwupdDevice + * + * Gets the time estimate for firmware installation (in seconds) + * + * Returns: the estimated time to flash this device (or 0 if unset) + * + * Since: 1.1.3 + **/ +guint32 +fwupd_device_get_install_duration (FwupdDevice *device) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (FWUPD_IS_DEVICE (device), 0); + return priv->install_duration; +} + +/** + * fwupd_device_set_install_duration: + * @device: A #FwupdDevice + * @duration: The amount of time + * + * Sets the time estimate for firmware installation (in seconds) + * + * Since: 1.1.3 + **/ +void +fwupd_device_set_install_duration (FwupdDevice *device, guint32 duration) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FWUPD_IS_DEVICE (device)); + priv->install_duration = duration; +} + +/** * fwupd_device_get_plugin: * @device: A #FwupdDevice * @@ -859,17 +1003,98 @@ } /** - * fwupd_device_to_variant: + * fwupd_device_incorporate: + * @self: A #FwupdDevice + * @donor: Another #FwupdDevice + * + * Copy all properties from the donor object if they have not already been set. + * + * Since: 1.1.0 + **/ +void +fwupd_device_incorporate (FwupdDevice *self, FwupdDevice *donor) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (self); + FwupdDevicePrivate *priv_donor = GET_PRIVATE (donor); + + if (priv->flags == 0) + fwupd_device_add_flag (self, priv_donor->flags); + if (priv->created == 0) + fwupd_device_set_created (self, priv_donor->created); + if (priv->modified == 0) + fwupd_device_set_modified (self, priv_donor->modified); + if (priv->flashes_left == 0) + fwupd_device_set_flashes_left (self, priv_donor->flashes_left); + if (priv->install_duration == 0) + fwupd_device_set_install_duration (self, priv_donor->install_duration); + if (priv->update_state == 0) + fwupd_device_set_update_state (self, priv_donor->update_state); + if (priv->description == NULL) + fwupd_device_set_description (self, priv_donor->description); + if (priv->id == NULL) + fwupd_device_set_id (self, priv_donor->id); + if (priv->parent_id == NULL) + fwupd_device_set_parent_id (self, priv_donor->parent_id); + if (priv->name == NULL) + fwupd_device_set_name (self, priv_donor->name); + if (priv->serial == NULL) + fwupd_device_set_serial (self, priv_donor->serial); + if (priv->summary == NULL) + fwupd_device_set_summary (self, priv_donor->summary); + if (priv->vendor == NULL) + fwupd_device_set_vendor (self, priv_donor->vendor); + if (priv->vendor_id == NULL) + fwupd_device_set_vendor_id (self, priv_donor->vendor_id); + if (priv->plugin == NULL) + 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) + fwupd_device_set_version_lowest (self, priv_donor->version_lowest); + if (priv->version_bootloader == NULL) + fwupd_device_set_version_bootloader (self, priv_donor->version_bootloader); + if (priv->version_format == FWUPD_VERSION_FORMAT_UNKNOWN) + fwupd_device_set_version_format (self, priv_donor->version_format); + for (guint i = 0; i < priv_donor->guids->len; i++) { + const gchar *tmp = g_ptr_array_index (priv_donor->guids, i); + fwupd_device_add_guid (self, tmp); + } + for (guint i = 0; i < priv_donor->instance_ids->len; i++) { + const gchar *tmp = g_ptr_array_index (priv_donor->instance_ids, i); + fwupd_device_add_instance_id (self, tmp); + } + for (guint i = 0; i < priv_donor->icons->len; i++) { + const gchar *tmp = g_ptr_array_index (priv_donor->icons, i); + fwupd_device_add_icon (self, tmp); + } + for (guint i = 0; i < priv_donor->checksums->len; i++) { + const gchar *tmp = g_ptr_array_index (priv_donor->checksums, i); + fwupd_device_add_checksum (self, tmp); + } + for (guint i = 0; i < priv_donor->releases->len; i++) { + FwupdRelease *tmp = g_ptr_array_index (priv_donor->releases, i); + fwupd_device_add_release (self, tmp); + } +} + +/** + * fwupd_device_to_variant_full: * @device: A #FwupdDevice + * @flags: #FwupdDeviceFlags for the call * * Creates a GVariant from the device data. + * Optionally provides additional data based upon flags * * Returns: the GVariant, or %NULL for error * - * Since: 1.0.0 + * Since: 1.1.2 **/ GVariant * -fwupd_device_to_variant (FwupdDevice *device) +fwupd_device_to_variant_full (FwupdDevice *device, FwupdDeviceFlags flags) { FwupdDevicePrivate *priv = GET_PRIVATE (device); GVariantBuilder builder; @@ -877,7 +1102,7 @@ g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); /* create an array with all the metadata in */ - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); if (priv->id != NULL) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_DEVICE_ID, @@ -978,16 +1203,44 @@ FWUPD_RESULT_KEY_FLASHES_LEFT, g_variant_new_uint32 (priv->flashes_left)); } + if (priv->install_duration > 0) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_INSTALL_DURATION, + g_variant_new_uint32 (priv->install_duration)); + } if (priv->update_error != NULL) { g_variant_builder_add (&builder, "{sv}", 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, g_variant_new_uint32 (priv->update_state)); } + if (priv->version_format != FWUPD_VERSION_FORMAT_UNKNOWN) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_VERSION_FORMAT, + g_variant_new_uint32 (priv->version_format)); + } + if (flags & FWUPD_DEVICE_FLAG_TRUSTED) { + if (priv->serial != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_SERIAL, + g_variant_new_string (priv->serial)); + } + if (priv->instance_ids->len > 0) { + const gchar * const *tmp = (const gchar * const *) priv->instance_ids->pdata; + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_INSTANCE_IDS, + g_variant_new_strv (tmp, priv->instance_ids->len)); + } + } /* create an array with all the metadata in */ if (priv->releases->len > 0) { @@ -1006,6 +1259,22 @@ return g_variant_new ("a{sv}", &builder); } +/** + * fwupd_device_to_variant: + * @device: A #FwupdDevice + * + * Creates a GVariant from the device data omitting sensitive fields + * + * Returns: the GVariant, or %NULL for error + * + * Since: 1.0.0 + **/ +GVariant * +fwupd_device_to_variant (FwupdDevice *device) +{ + return fwupd_device_to_variant_full (device, FWUPD_DEVICE_FLAG_NONE); +} + static void fwupd_device_from_key_value (FwupdDevice *device, const gchar *key, GVariant *value) { @@ -1047,6 +1316,12 @@ fwupd_device_add_guid (device, guids[i]); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_INSTANCE_IDS) == 0) { + g_autofree const gchar **instance_ids = g_variant_get_strv (value, NULL); + for (guint i = 0; instance_ids != NULL && instance_ids[i] != NULL; i++) + fwupd_device_add_instance_id (device, instance_ids[i]); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_ICON) == 0) { g_autofree const gchar **icons = g_variant_get_strv (value, NULL); for (guint i = 0; icons != NULL && icons[i] != NULL; i++) @@ -1065,6 +1340,10 @@ fwupd_device_set_vendor_id (device, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_SERIAL) == 0) { + fwupd_device_set_serial (device, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_SUMMARY) == 0) { fwupd_device_set_summary (device, g_variant_get_string (value, NULL)); return; @@ -1102,14 +1381,26 @@ fwupd_device_set_flashes_left (device, g_variant_get_uint32 (value)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_INSTALL_DURATION) == 0) { + fwupd_device_set_install_duration (device, g_variant_get_uint32 (value)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_UPDATE_ERROR) == 0) { 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; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_VERSION_FORMAT) == 0) { + fwupd_device_set_version_format (device, g_variant_get_uint32 (value)); + return; + } } static void @@ -1205,6 +1496,77 @@ } /** + * fwupd_device_get_version_format: + * @device: A #FwupdDevice + * + * Gets the update state. + * + * Returns: the update state, or %FWUPD_VERSION_FORMAT_UNKNOWN if unset + * + * Since: 1.2.9 + **/ +FwupdVersionFormat +fwupd_device_get_version_format (FwupdDevice *device) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (FWUPD_IS_DEVICE (device), FWUPD_VERSION_FORMAT_UNKNOWN); + return priv->version_format; +} + +/** + * fwupd_device_set_version_format: + * @device: A #FwupdDevice + * @version_format: the state, e.g. %FWUPD_VERSION_FORMAT_PENDING + * + * Sets the update state. + * + * Since: 1.2.9 + **/ +void +fwupd_device_set_version_format (FwupdDevice *device, FwupdVersionFormat version_format) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FWUPD_IS_DEVICE (device)); + priv->version_format = version_format; +} + +/** + * 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 * @@ -1303,6 +1665,115 @@ fwupd_pad_kv_str (str, key, fwupd_update_state_to_string (value)); } +static void +fwupd_device_json_add_string (JsonBuilder *builder, const gchar *key, const gchar *str) +{ + if (str == NULL) + return; + json_builder_set_member_name (builder, key); + json_builder_add_string_value (builder, str); +} + +static void +fwupd_device_json_add_int (JsonBuilder *builder, const gchar *key, guint64 num) +{ + if (num == 0) + return; + json_builder_set_member_name (builder, key); + json_builder_add_int_value (builder, num); +} + +/** + * fwupd_device_to_json: + * @device: A #FwupdDevice + * @builder: A #JsonBuilder + * + * Adds a fwupd device to a JSON builder + * + * Since: 1.2.6 + **/ +void +fwupd_device_to_json (FwupdDevice *device, JsonBuilder *builder) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + + g_return_if_fail (FWUPD_IS_DEVICE (device)); + g_return_if_fail (builder != NULL); + + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_DEVICE_ID, priv->id); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_PARENT_DEVICE_ID, + priv->parent_id); + if (priv->guids->len > 0) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_GUID); + json_builder_begin_array (builder); + for (guint i = 0; i < priv->guids->len; i++) { + const gchar *guid = g_ptr_array_index (priv->guids, i); + json_builder_add_string_value (builder, guid); + } + json_builder_end_array (builder); + } + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_SERIAL, priv->serial); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_SUMMARY, priv->summary); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); + if (priv->flags != FWUPD_DEVICE_FLAG_NONE) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_FLAGS); + json_builder_begin_array (builder); + for (guint i = 0; i < 64; i++) { + const gchar *tmp; + if ((priv->flags & ((guint64) 1 << i)) == 0) + continue; + tmp = fwupd_device_flag_to_string ((guint64) 1 << i); + json_builder_add_string_value (builder, tmp); + } + json_builder_end_array (builder); + } + if (priv->checksums->len > 0) { + json_builder_set_member_name (builder, "Checksums"); + json_builder_begin_array (builder); + for (guint i = 0; i < priv->checksums->len; i++) { + const gchar *checksum = g_ptr_array_index (priv->checksums, i); + json_builder_add_string_value (builder, checksum); + } + json_builder_end_array (builder); + } + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_VENDOR, priv->vendor); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_VENDOR_ID, priv->vendor_id); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_VERSION, priv->version); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_VERSION_LOWEST, priv->version_lowest); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_VERSION_BOOTLOADER, priv->version_bootloader); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_VERSION_FORMAT, + fwupd_version_format_to_string (priv->version_format)); + fwupd_device_json_add_int (builder, FWUPD_RESULT_KEY_FLASHES_LEFT, priv->flashes_left); + if (priv->icons->len > 0) { + json_builder_set_member_name (builder, "Icons"); + json_builder_begin_array (builder); + for (guint i = 0; i < priv->icons->len; i++) { + const gchar *icon = g_ptr_array_index (priv->icons, i); + json_builder_add_string_value (builder, icon); + } + json_builder_end_array (builder); + } + fwupd_device_json_add_int (builder, FWUPD_RESULT_KEY_INSTALL_DURATION, priv->install_duration); + fwupd_device_json_add_int (builder, FWUPD_RESULT_KEY_CREATED, priv->created); + fwupd_device_json_add_int (builder, FWUPD_RESULT_KEY_MODIFIED, priv->modified); + fwupd_device_json_add_int (builder, FWUPD_RESULT_KEY_UPDATE_STATE, priv->update_state); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_UPDATE_ERROR, priv->update_error); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_UPDATE_MESSAGE, priv->update_message); + if (priv->releases->len > 0) { + json_builder_set_member_name (builder, "Releases"); + json_builder_begin_array (builder); + for (guint i = 0; i < priv->releases->len; i++) { + FwupdRelease *release = g_ptr_array_index (priv->releases, i); + json_builder_begin_object (builder); + fwupd_release_to_json (release, builder); + json_builder_end_object (builder); + } + json_builder_end_array (builder); + } +} + /** * fwupd_device_to_string: * @device: A #FwupdDevice @@ -1318,6 +1789,7 @@ { FwupdDevicePrivate *priv = GET_PRIVATE (device); GString *str; + g_autoptr(GHashTable) ids = NULL; g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); @@ -1328,10 +1800,25 @@ str = g_string_append (str, "Unknown Device\n"); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DEVICE_ID, priv->id); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PARENT_DEVICE_ID, priv->parent_id); + ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + for (guint i = 0; i < priv->instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index (priv->instance_ids, i); + g_hash_table_insert (ids, + fwupd_guid_hash_string (instance_id), + g_strdup (instance_id)); + } for (guint i = 0; i < priv->guids->len; i++) { const gchar *guid = g_ptr_array_index (priv->guids, i); - fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_GUID, guid); + const gchar *instance_id = g_hash_table_lookup (ids, guid); + if (instance_id == NULL) { + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_GUID, guid); + } else { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("%s <- %s", guid, instance_id); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_GUID, tmp); + } } + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_SERIAL, priv->serial); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_SUMMARY, priv->summary); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); @@ -1346,6 +1833,8 @@ fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_VERSION, priv->version); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_VERSION_LOWEST, priv->version_lowest); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_VERSION_BOOTLOADER, priv->version_bootloader); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_VERSION_FORMAT, + fwupd_version_format_to_string (priv->version_format)); if (priv->flashes_left < 2) fwupd_pad_kv_int (str, FWUPD_RESULT_KEY_FLASHES_LEFT, priv->flashes_left); if (priv->icons->len > 0) { @@ -1358,10 +1847,12 @@ g_string_truncate (tmp, tmp->len - 1); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_ICON, tmp->str); } + fwupd_pad_kv_int (str, FWUPD_RESULT_KEY_INSTALL_DURATION, priv->install_duration); fwupd_pad_kv_unx (str, FWUPD_RESULT_KEY_CREATED, priv->created); 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); @@ -1373,10 +1864,53 @@ } static void +fwupd_device_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FwupdDevice *self = FWUPD_DEVICE (object); + FwupdDevicePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_VERSION_FORMAT: + g_value_set_uint (value, priv->version_format); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fwupd_device_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FwupdDevice *self = FWUPD_DEVICE (object); + switch (prop_id) { + case PROP_VERSION_FORMAT: + fwupd_device_set_version_format (self, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void fwupd_device_class_init (FwupdDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + object_class->finalize = fwupd_device_finalize; + object_class->get_property = fwupd_device_get_property; + object_class->set_property = fwupd_device_set_property; + + pspec = g_param_spec_uint ("version-format", NULL, NULL, + FWUPD_VERSION_FORMAT_UNKNOWN, + FWUPD_VERSION_FORMAT_LAST, + FWUPD_VERSION_FORMAT_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_VERSION_FORMAT, pspec); } static void @@ -1384,6 +1918,7 @@ { FwupdDevicePrivate *priv = GET_PRIVATE (device); priv->guids = g_ptr_array_new_with_free_func (g_free); + priv->instance_ids = g_ptr_array_new_with_free_func (g_free); priv->icons = g_ptr_array_new_with_free_func (g_free); priv->checksums = g_ptr_array_new_with_free_func (g_free); priv->releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); @@ -1401,15 +1936,18 @@ g_free (priv->id); g_free (priv->parent_id); g_free (priv->name); + g_free (priv->serial); g_free (priv->summary); g_free (priv->vendor); 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); g_ptr_array_unref (priv->guids); + g_ptr_array_unref (priv->instance_ids); g_ptr_array_unref (priv->icons); g_ptr_array_unref (priv->checksums); g_ptr_array_unref (priv->releases); @@ -1430,30 +1968,30 @@ /** * fwupd_device_from_variant: - * @data: a #GVariant + * @value: a #GVariant * * Creates a new device using packed data. * - * Returns: (transfer full): a new #FwupdDevice, or %NULL if @data was invalid + * Returns: (transfer full): a new #FwupdDevice, or %NULL if @value was invalid * * Since: 1.0.0 **/ FwupdDevice * -fwupd_device_from_variant (GVariant *data) +fwupd_device_from_variant (GVariant *value) { FwupdDevice *dev = NULL; const gchar *type_string; g_autoptr(GVariantIter) iter = NULL; /* format from GetDetails */ - type_string = g_variant_get_type_string (data); + type_string = g_variant_get_type_string (value); if (g_strcmp0 (type_string, "(a{sv})") == 0) { dev = fwupd_device_new (); - g_variant_get (data, "(a{sv})", &iter); + g_variant_get (value, "(a{sv})", &iter); fwupd_device_set_from_variant_iter (dev, iter); } else if (g_strcmp0 (type_string, "a{sv}") == 0) { dev = fwupd_device_new (); - g_variant_get (data, "a{sv}", &iter); + g_variant_get (value, "a{sv}", &iter); fwupd_device_set_from_variant_iter (dev, iter); } else { g_warning ("type %s not known", type_string); @@ -1462,6 +2000,78 @@ } /** + * fwupd_device_array_from_variant: + * @value: a #GVariant + * + * Creates an array of new devices using packed data. + * + * Returns: (transfer container) (element-type FwupdDevice): devices, or %NULL if @value was invalid + * + * Since: 1.2.10 + **/ +GPtrArray * +fwupd_device_array_from_variant (GVariant *value) +{ + GPtrArray *array = NULL; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + g_autoptr(GHashTable) devices_by_id = NULL; + + array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + devices_by_id = g_hash_table_new (g_str_hash, g_str_equal); + untuple = g_variant_get_child_value (value, 0); + sz = g_variant_n_children (untuple); + for (guint i = 0; i < sz; i++) { + FwupdDevice *dev; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value (untuple, i); + dev = fwupd_device_from_variant (data); + if (dev == NULL) + continue; + g_ptr_array_add (array, dev); + if (fwupd_device_get_id (dev) != NULL) { + g_hash_table_insert (devices_by_id, + (gpointer) fwupd_device_get_id (dev), + (gpointer) dev); + } + } + + /* set the parent on each child */ + for (guint i = 0; i < array->len; i++) { + FwupdDevice *dev = g_ptr_array_index (array, i); + const gchar *parent_id = fwupd_device_get_parent_id (dev); + if (parent_id != NULL) { + FwupdDevice *dev_tmp; + dev_tmp = g_hash_table_lookup (devices_by_id, parent_id); + fwupd_device_set_parent (dev, dev_tmp); + } + } + + return array; +} + +/** + * fwupd_device_compare: + * @device1: a #FwupdDevice + * @device2: a #FwupdDevice + * + * Comparison function for comparing two FwupdDevice objects. + * + * Returns: negative, 0 or positive + * + * Since: 1.1.1 + **/ +gint +fwupd_device_compare (FwupdDevice *device1, FwupdDevice *device2) +{ + FwupdDevicePrivate *priv1 = GET_PRIVATE (device1); + FwupdDevicePrivate *priv2 = GET_PRIVATE (device2); + g_return_val_if_fail (FWUPD_IS_DEVICE (device1), 0); + g_return_val_if_fail (FWUPD_IS_DEVICE (device2), 0); + return g_strcmp0 (priv1->id, priv2->id); +} + +/** * fwupd_device_new: * * Creates a new device. diff -Nru fwupd-1.0.9/libfwupd/fwupd-device.h fwupd-1.2.10/libfwupd/fwupd-device.h --- fwupd-1.0.9/libfwupd/fwupd-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_DEVICE_H -#define __FWUPD_DEVICE_H +#pragma once #include @@ -46,6 +44,9 @@ const gchar *fwupd_device_get_name (FwupdDevice *device); void fwupd_device_set_name (FwupdDevice *device, const gchar *name); +const gchar *fwupd_device_get_serial (FwupdDevice *device); +void fwupd_device_set_serial (FwupdDevice *device, + const gchar *serial); const gchar *fwupd_device_get_summary (FwupdDevice *device); void fwupd_device_set_summary (FwupdDevice *device, const gchar *summary); @@ -61,9 +62,15 @@ const gchar *fwupd_device_get_version_bootloader (FwupdDevice *device); void fwupd_device_set_version_bootloader (FwupdDevice *device, const gchar *version_bootloader); +FwupdVersionFormat fwupd_device_get_version_format (FwupdDevice *device); +void fwupd_device_set_version_format (FwupdDevice *device, + FwupdVersionFormat version_format); guint32 fwupd_device_get_flashes_left (FwupdDevice *device); void fwupd_device_set_flashes_left (FwupdDevice *device, guint32 flashes_left); +guint32 fwupd_device_get_install_duration (FwupdDevice *device); +void fwupd_device_set_install_duration (FwupdDevice *device, + guint32 duration); guint64 fwupd_device_get_flags (FwupdDevice *device); void fwupd_device_set_flags (FwupdDevice *device, guint64 flags); @@ -97,6 +104,11 @@ const gchar *guid); GPtrArray *fwupd_device_get_guids (FwupdDevice *device); const gchar *fwupd_device_get_guid_default (FwupdDevice *device); +void fwupd_device_add_instance_id (FwupdDevice *device, + const gchar *instance_id); +gboolean fwupd_device_has_instance_id (FwupdDevice *device, + const gchar *instance_id); +GPtrArray *fwupd_device_get_instance_ids (FwupdDevice *device); void fwupd_device_add_icon (FwupdDevice *device, const gchar *icon); GPtrArray *fwupd_device_get_icons (FwupdDevice *device); @@ -107,12 +119,17 @@ 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); FwupdRelease *fwupd_device_get_release_default (FwupdDevice *device); +gint fwupd_device_compare (FwupdDevice *device1, + FwupdDevice *device2); -G_END_DECLS - -#endif /* __FWUPD_DEVICE_H */ +FwupdDevice *fwupd_device_from_variant (GVariant *value); +GPtrArray *fwupd_device_array_from_variant (GVariant *value); +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-device-private.h fwupd-1.2.10/libfwupd/fwupd-device-private.h --- fwupd-1.0.9/libfwupd/fwupd-device-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-device-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,23 +1,25 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_DEVICE_PRIVATE_H -#define __FWUPD_DEVICE_PRIVATE_H +#pragma once #include +#include #include "fwupd-device.h" G_BEGIN_DECLS -FwupdDevice *fwupd_device_from_variant (GVariant *data); GVariant *fwupd_device_to_variant (FwupdDevice *device); +GVariant *fwupd_device_to_variant_full (FwupdDevice *device, + FwupdDeviceFlags flags); +void fwupd_device_incorporate (FwupdDevice *self, + FwupdDevice *donor); +void fwupd_device_to_json (FwupdDevice *device, + JsonBuilder *builder); G_END_DECLS -#endif /* __FWUPD_DEVICE_PRIVATE_H */ - diff -Nru fwupd-1.0.9/libfwupd/fwupd-enums.c fwupd-1.2.10/libfwupd/fwupd-enums.c --- fwupd-1.0.9/libfwupd/fwupd-enums.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-enums.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -58,6 +57,8 @@ return "downloading"; if (status == FWUPD_STATUS_WAITING_FOR_AUTH) return "waiting-for-auth"; + if (status == FWUPD_STATUS_SHUTDOWN) + return "shutdown"; return NULL; } @@ -100,6 +101,8 @@ 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; } @@ -136,6 +139,8 @@ 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) @@ -146,6 +151,18 @@ return "install-parent-first"; if (device_flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) return "is-bootloader"; + if (device_flag == FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) + return "wait-for-replug"; + if (device_flag == FWUPD_DEVICE_FLAG_IGNORE_VALIDATION) + return "ignore-validation"; + if (device_flag == FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED) + return "another-write-required"; + if (device_flag == FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS) + return "no-auto-instance-ids"; + if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) + return "needs-activation"; + if (device_flag == FWUPD_DEVICE_FLAG_ENSURE_SEMVER) + return "ensure-semver"; if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) return "unknown"; return NULL; @@ -186,6 +203,8 @@ 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) @@ -196,6 +215,18 @@ return FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST; if (g_strcmp0 (device_flag, "is-bootloader") == 0) return FWUPD_DEVICE_FLAG_IS_BOOTLOADER; + if (g_strcmp0 (device_flag, "wait-for-replug") == 0) + return FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG; + if (g_strcmp0 (device_flag, "ignore-validation") == 0) + return FWUPD_DEVICE_FLAG_IGNORE_VALIDATION; + if (g_strcmp0 (device_flag, "another-write-required") == 0) + return FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED; + if (g_strcmp0 (device_flag, "no-auto-instance-ids") == 0) + return FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS; + if (g_strcmp0 (device_flag, "needs-activation") == 0) + return FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION; + if (g_strcmp0 (device_flag, "ensure-semver") == 0) + return FWUPD_DEVICE_FLAG_ENSURE_SEMVER; return FWUPD_DEVICE_FLAG_UNKNOWN; } @@ -220,6 +251,8 @@ return "success"; if (update_state == FWUPD_UPDATE_STATE_FAILED) return "failed"; + if (update_state == FWUPD_UPDATE_STATE_FAILED_TRANSIENT) + return "failed-transient"; if (update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT) return "needs-reboot"; return NULL; @@ -246,6 +279,8 @@ return FWUPD_UPDATE_STATE_SUCCESS; if (g_strcmp0 (update_state, "failed") == 0) return FWUPD_UPDATE_STATE_FAILED; + if (g_strcmp0 (update_state, "failed-transient") == 0) + return FWUPD_UPDATE_STATE_FAILED_TRANSIENT; if (g_strcmp0 (update_state, "needs-reboot") == 0) return FWUPD_UPDATE_STATE_NEEDS_REBOOT; return FWUPD_UPDATE_STATE_UNKNOWN; @@ -338,3 +373,125 @@ return "pkcs7"; return NULL; } + +/** + * fwupd_release_flag_to_string: + * @release_flag: A #FwupdReleaseFlags, e.g. %FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD + * + * Converts a #FwupdReleaseFlags to a string. + * + * Return value: identifier string + * + * Since: 1.2.6 + **/ +const gchar * +fwupd_release_flag_to_string (FwupdReleaseFlags release_flag) +{ + if (release_flag == FWUPD_RELEASE_FLAG_NONE) + return "none"; + if (release_flag == FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD) + return "trusted-payload"; + if (release_flag == FWUPD_RELEASE_FLAG_TRUSTED_METADATA) + return "trusted-metadata"; + if (release_flag == FWUPD_RELEASE_FLAG_IS_UPGRADE) + return "is-upgrade"; + if (release_flag == FWUPD_RELEASE_FLAG_IS_DOWNGRADE) + return "is-downgrade"; + if (release_flag == FWUPD_RELEASE_FLAG_BLOCKED_VERSION) + return "blocked-version"; + if (release_flag == FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL) + return "blocked-approval"; + return NULL; +} + +/** + * fwupd_release_flag_from_string: + * @release_flag: A string, e.g. `trusted-payload` + * + * Converts a string to a #FwupdReleaseFlags. + * + * Return value: enumerated value + * + * Since: 1.2.6 + **/ +FwupdReleaseFlags +fwupd_release_flag_from_string (const gchar *release_flag) +{ + if (g_strcmp0 (release_flag, "trusted-payload") == 0) + return FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD; + if (g_strcmp0 (release_flag, "trusted-metadata") == 0) + return FWUPD_RELEASE_FLAG_TRUSTED_METADATA; + if (g_strcmp0 (release_flag, "is-upgrade") == 0) + return FWUPD_RELEASE_FLAG_IS_UPGRADE; + if (g_strcmp0 (release_flag, "is-downgrade") == 0) + return FWUPD_RELEASE_FLAG_IS_DOWNGRADE; + if (g_strcmp0 (release_flag, "blocked-version") == 0) + return FWUPD_RELEASE_FLAG_BLOCKED_VERSION; + if (g_strcmp0 (release_flag, "blocked-approval") == 0) + return FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL; + return FWUPD_RELEASE_FLAG_NONE; +} + +/** + * fwupd_version_format_from_string: + * @str: A string, e.g. `quad` + * + * Converts text to a display version type. + * + * Returns: A #FwupdVersionFormat, e.g. %FWUPD_VERSION_FORMAT_TRIPLET + * + * Since: 1.2.9 + **/ +FwupdVersionFormat +fwupd_version_format_from_string (const gchar *str) +{ + if (g_strcmp0 (str, "plain") == 0) + return FWUPD_VERSION_FORMAT_PLAIN; + if (g_strcmp0 (str, "pair") == 0) + return FWUPD_VERSION_FORMAT_PAIR; + if (g_strcmp0 (str, "number") == 0) + return FWUPD_VERSION_FORMAT_NUMBER; + if (g_strcmp0 (str, "triplet") == 0) + return FWUPD_VERSION_FORMAT_TRIPLET; + if (g_strcmp0 (str, "quad") == 0) + return FWUPD_VERSION_FORMAT_QUAD; + if (g_strcmp0 (str, "bcd") == 0) + return FWUPD_VERSION_FORMAT_BCD; + if (g_strcmp0 (str, "intel-me") == 0) + return FWUPD_VERSION_FORMAT_INTEL_ME; + if (g_strcmp0 (str, "intel-me2") == 0) + return FWUPD_VERSION_FORMAT_INTEL_ME2; + return FWUPD_VERSION_FORMAT_UNKNOWN; +} + +/** + * fwupd_version_format_to_string: + * @kind: A #FwupdVersionFormat, e.g. %FWUPD_VERSION_FORMAT_TRIPLET + * + * Converts a display version type to text. + * + * Returns: A string, e.g. `quad`, or %NULL if not known + * + * Since: 1.2.9 + **/ +const gchar * +fwupd_version_format_to_string (FwupdVersionFormat kind) +{ + if (kind == FWUPD_VERSION_FORMAT_PLAIN) + return "plain"; + if (kind == FWUPD_VERSION_FORMAT_NUMBER) + return "number"; + if (kind == FWUPD_VERSION_FORMAT_PAIR) + return "pair"; + if (kind == FWUPD_VERSION_FORMAT_TRIPLET) + return "triplet"; + if (kind == FWUPD_VERSION_FORMAT_QUAD) + return "quad"; + if (kind == FWUPD_VERSION_FORMAT_BCD) + return "bcd"; + if (kind == FWUPD_VERSION_FORMAT_INTEL_ME) + return "intel-me"; + if (kind == FWUPD_VERSION_FORMAT_INTEL_ME2) + return "intel-me2"; + return NULL; +} diff -Nru fwupd-1.0.9/libfwupd/fwupd-enums.h fwupd-1.2.10/libfwupd/fwupd-enums.h --- fwupd-1.0.9/libfwupd/fwupd-enums.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-enums.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_ENUMS_H -#define __FWUPD_ENUMS_H +#pragma once #include +G_BEGIN_DECLS + /** * FwupdStatus: * @FWUPD_STATUS_UNKNOWN: Unknown state @@ -25,6 +25,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. **/ @@ -42,6 +43,7 @@ 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; @@ -79,6 +81,14 @@ * @FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION: Always use the runtime version rather than the bootloader * @FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST: Install composite firmware on the parent before the child * @FWUPD_DEVICE_FLAG_IS_BOOTLOADER: Is currently in bootloader mode + * @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 + * @FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED: Requires the update to be retried with a new plugin + * @FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS: Do not add instance IDs from the device baseclass + * @FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION: Device update needs to be separately activated + * @FWUPD_DEVICE_FLAG_ENSURE_SEMVER: Ensure the version is a valid semantic version, e.g. numbers separated with dots * * The device flags. **/ @@ -97,10 +107,40 @@ #define FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION (1u << 11) /* Since: 1.0.6 */ #define FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST (1u << 12) /* Since: 1.0.8 */ #define FWUPD_DEVICE_FLAG_IS_BOOTLOADER (1u << 13) /* Since: 1.0.8 */ +#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_ANOTHER_WRITE_REQUIRED (1u << 18) /* Since: 1.2.5 */ +#define FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS (1u << 19) /* Since: 1.2.5 */ +#define FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION (1u << 20) /* Since: 1.2.6 */ +#define FWUPD_DEVICE_FLAG_ENSURE_SEMVER (1u << 21) /* Since: 1.2.9 */ #define FWUPD_DEVICE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 0.7.3 */ typedef guint64 FwupdDeviceFlags; /** + * FwupdReleaseFlags: + * @FWUPD_RELEASE_FLAG_NONE: No flags set + * @FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD: The payload binary is trusted + * @FWUPD_RELEASE_FLAG_TRUSTED_METADATA: The payload metadata is trusted + * @FWUPD_RELEASE_FLAG_IS_UPGRADE: Is newer than the device version + * @FWUPD_RELEASE_FLAG_IS_DOWNGRADE: Is older than the device version + * @FWUPD_RELEASE_FLAG_BLOCKED_VERSION: Blocked as below device version-lowest + * @FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL: Blocked as release not approved + * + * The release flags. + **/ +#define FWUPD_RELEASE_FLAG_NONE (0u) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD (1u << 0) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_TRUSTED_METADATA (1u << 1) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_IS_UPGRADE (1u << 2) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_IS_DOWNGRADE (1u << 3) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_BLOCKED_VERSION (1u << 4) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL (1u << 5) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 1.2.6 */ +typedef guint64 FwupdReleaseFlags; + +/** * FwupdInstallFlags: * @FWUPD_INSTALL_FLAG_NONE: No flags set * @FWUPD_INSTALL_FLAG_OFFLINE: Schedule this for next boot @@ -123,12 +163,29 @@ } FwupdInstallFlags; /** + * FwupdSelfSignFlags: + * @FWUPD_SELF_SIGN_FLAG_NONE: No flags set + * @FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP: Add the timestamp to the detached signature + * @FWUPD_SELF_SIGN_FLAG_ADD_CERT: Add the certificate to the detached signature + * + * Flags to set when performing the firwmare update or install. + **/ +typedef enum { + FWUPD_SELF_SIGN_FLAG_NONE = 0, /* Since: 1.2.6 */ + FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP = 1 << 0, /* Since: 1.2.6 */ + FWUPD_SELF_SIGN_FLAG_ADD_CERT = 1 << 1, /* Since: 1.2.6 */ + /*< private >*/ + FWUPD_SELF_SIGN_FLAG_LAST +} FwupdSelfSignFlags; + +/** * FwupdUpdateState: * @FWUPD_UPDATE_STATE_UNKNOWN: Unknown * @FWUPD_UPDATE_STATE_PENDING: Update is pending - * @FWUPD_UPDATE_STATE_SUCCESS: Update was successfull + * @FWUPD_UPDATE_STATE_SUCCESS: Update was successful * @FWUPD_UPDATE_STATE_FAILED: Update failed * @FWUPD_UPDATE_STATE_NEEDS_REBOOT: Waiting for a reboot to apply + * @FWUPD_UPDATE_STATE_FAILED_TRANSIENT: Update failed due to transient issue, e.g. AC power required * * The update state. **/ @@ -138,6 +195,7 @@ FWUPD_UPDATE_STATE_SUCCESS, /* Since: 0.7.0 */ FWUPD_UPDATE_STATE_FAILED, /* Since: 0.7.0 */ FWUPD_UPDATE_STATE_NEEDS_REBOOT, /* Since: 1.0.4 */ + FWUPD_UPDATE_STATE_FAILED_TRANSIENT, /* Since: 1.2.7 */ /*< private >*/ FWUPD_UPDATE_STATE_LAST } FwupdUpdateState; @@ -160,15 +218,50 @@ FWUPD_KEYRING_KIND_LAST } FwupdKeyringKind; +/** + * FwupdVersionFormat: + * @FWUPD_VERSION_FORMAT_UNKNOWN: Unknown version format + * @FWUPD_VERSION_FORMAT_PLAIN: An unidentified format text string + * @FWUPD_VERSION_FORMAT_NUMBER: A single integer version number + * @FWUPD_VERSION_FORMAT_PAIR: Two AABB.CCDD version numbers + * @FWUPD_VERSION_FORMAT_TRIPLET: Microsoft-style AA.BB.CCDD version numbers + * @FWUPD_VERSION_FORMAT_QUAD: Dell-style AA.BB.CC.DD version numbers + * @FWUPD_VERSION_FORMAT_BCD: Binary coded decimal notation + * @FWUPD_VERSION_FORMAT_INTEL_ME: Intel ME-style bitshifted notation + * @FWUPD_VERSION_FORMAT_INTEL_ME2: Intel ME-style A.B.CC.DDDD notation notation + * + * The flags used when parsing version numbers. + * + * If no verification is required then %FWUPD_VERSION_FORMAT_PLAIN should + * be used to signify an unparsable text string. + **/ +typedef enum { + FWUPD_VERSION_FORMAT_UNKNOWN, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_PLAIN, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_NUMBER, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_PAIR, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_TRIPLET, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_QUAD, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_BCD, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_INTEL_ME, /* Since: 1.2.9 */ + FWUPD_VERSION_FORMAT_INTEL_ME2, /* Since: 1.2.9 */ + /*< private >*/ + FWUPD_VERSION_FORMAT_LAST +} FwupdVersionFormat; + const gchar *fwupd_status_to_string (FwupdStatus status); FwupdStatus fwupd_status_from_string (const gchar *status); const gchar *fwupd_device_flag_to_string (FwupdDeviceFlags device_flag); FwupdDeviceFlags fwupd_device_flag_from_string (const gchar *device_flag); +const gchar *fwupd_release_flag_to_string (FwupdReleaseFlags release_flag); +FwupdReleaseFlags fwupd_release_flag_from_string (const gchar *release_flag); const gchar *fwupd_update_state_to_string (FwupdUpdateState update_state); FwupdUpdateState fwupd_update_state_from_string (const gchar *update_state); const gchar *fwupd_trust_flag_to_string (FwupdTrustFlags trust_flag); FwupdTrustFlags fwupd_trust_flag_from_string (const gchar *trust_flag); FwupdKeyringKind fwupd_keyring_kind_from_string (const gchar *keyring_kind); const gchar *fwupd_keyring_kind_to_string (FwupdKeyringKind keyring_kind); +FwupdVersionFormat fwupd_version_format_from_string (const gchar *str); +const gchar *fwupd_version_format_to_string (FwupdVersionFormat kind); -#endif /* __FWUPD_ENUMS_H */ +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-enums-private.h fwupd-1.2.10/libfwupd/fwupd-enums-private.h --- fwupd-1.0.9/libfwupd/fwupd-enums-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-enums-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,12 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes +/* + * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_ENUMS_PRIVATE_H -#define __FWUPD_ENUMS_PRIVATE_H +#pragma once + +G_BEGIN_DECLS #define FWUPD_RESULT_KEY_APPSTREAM_ID "AppstreamId" /* s */ #define FWUPD_RESULT_KEY_CHECKSUM "Checksum" /* as */ @@ -15,10 +15,16 @@ #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_CATEGORIES "Categories" /* as */ #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_INSTANCE_IDS "InstanceIds" /* 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 */ @@ -27,9 +33,11 @@ #define FWUPD_RESULT_KEY_PLUGIN "Plugin" /* s */ #define FWUPD_RESULT_KEY_RELEASE "Release" /* a{sv} */ #define FWUPD_RESULT_KEY_REMOTE_ID "RemoteId" /* s */ +#define FWUPD_RESULT_KEY_SERIAL "Serial" /* s */ #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 */ @@ -37,7 +45,8 @@ #define FWUPD_RESULT_KEY_VENDOR "Vendor" /* s */ #define FWUPD_RESULT_KEY_VENDOR "Vendor" /* s */ #define FWUPD_RESULT_KEY_VERSION_BOOTLOADER "VersionBootloader" /* s */ +#define FWUPD_RESULT_KEY_VERSION_FORMAT "VersionFormat" /* u */ #define FWUPD_RESULT_KEY_VERSION_LOWEST "VersionLowest" /* s */ #define FWUPD_RESULT_KEY_VERSION "Version" /* s */ -#endif /* __FWUPD_ENUMS_PRIVATE_H */ +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-error.c fwupd-1.2.10/libfwupd/fwupd-error.c --- fwupd-1.0.9/libfwupd/fwupd-error.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-error.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -64,6 +63,10 @@ return FWUPD_DBUS_INTERFACE ".AcPowerRequired"; if (error == FWUPD_ERROR_PERMISSION_DENIED) return FWUPD_DBUS_INTERFACE ".PermissionDenied"; + if (error == FWUPD_ERROR_BROKEN_SYSTEM) + return FWUPD_DBUS_INTERFACE ".BrokenSystem"; + if (error == FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW) + return FWUPD_DBUS_INTERFACE ".BatteryLevelTooLow"; return NULL; } @@ -108,6 +111,10 @@ return FWUPD_ERROR_AC_POWER_REQUIRED; if (g_strcmp0 (error, FWUPD_DBUS_INTERFACE ".PermissionDenied") == 0) return FWUPD_ERROR_PERMISSION_DENIED; + if (g_strcmp0 (error, FWUPD_DBUS_INTERFACE ".BrokenSystem") == 0) + return FWUPD_ERROR_BROKEN_SYSTEM; + if (g_strcmp0 (error, FWUPD_DBUS_INTERFACE ".BatteryLevelTooLow") == 0) + return FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW; return FWUPD_ERROR_LAST; } diff -Nru fwupd-1.0.9/libfwupd/fwupd-error.h fwupd-1.2.10/libfwupd/fwupd-error.h --- fwupd-1.0.9/libfwupd/fwupd-error.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-error.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_ERROR_H -#define __FWUPD_ERROR_H +#pragma once #include +G_BEGIN_DECLS + #define FWUPD_ERROR fwupd_error_quark() /** @@ -28,6 +28,8 @@ * @FWUPD_ERROR_SIGNATURE_INVALID: Signature was invalid * @FWUPD_ERROR_AC_POWER_REQUIRED: AC power was required * @FWUPD_ERROR_PERMISSION_DENIED: Permission was denied + * @FWUPD_ERROR_BROKEN_SYSTEM: User has configured their system in a broken way + * @FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW: The system battery level is too low * * The error code. **/ @@ -46,6 +48,8 @@ FWUPD_ERROR_SIGNATURE_INVALID, /* Since: 0.1.2 */ FWUPD_ERROR_AC_POWER_REQUIRED, /* Since: 0.8.0 */ FWUPD_ERROR_PERMISSION_DENIED, /* Since: 0.9.8 */ + FWUPD_ERROR_BROKEN_SYSTEM, /* Since: 1.2.8 */ + FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW, /* Since: 1.2.10 */ /*< private >*/ FWUPD_ERROR_LAST } FwupdError; @@ -54,4 +58,4 @@ const gchar *fwupd_error_to_string (FwupdError error); FwupdError fwupd_error_from_string (const gchar *error); -#endif /* __FWUPD_ERROR_H */ +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd.h fwupd-1.2.10/libfwupd/fwupd.h --- fwupd-1.0.9/libfwupd/fwupd.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,18 +1,16 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + /** * SECTION:fwupd * @short_description: Helper objects for accessing fwupd */ -#ifndef __FWUPD_H__ -#define __FWUPD_H__ - #define __FWUPD_H_INSIDE__ #include @@ -29,6 +27,3 @@ #endif #undef __FWUPD_H_INSIDE__ - -#endif /* __FWUPD_H__ */ - diff -Nru fwupd-1.0.9/libfwupd/fwupd.map fwupd-1.2.10/libfwupd/fwupd.map --- fwupd-1.0.9/libfwupd/fwupd.map 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd.map 2019-07-15 18:25:54.000000000 +0000 @@ -252,3 +252,122 @@ fwupd_device_set_parent_id; local: *; } LIBFWUPD_1.0.7; + +LIBFWUPD_1.1.0 { + global: + fwupd_device_incorporate; + local: *; +} LIBFWUPD_1.0.8; + +LIBFWUPD_1.1.1 { + global: + fwupd_device_compare; + local: *; +} LIBFWUPD_1.1.0; + +LIBFWUPD_1.1.2 { + global: + fwupd_device_get_serial; + fwupd_device_set_serial; + fwupd_device_to_variant_full; + local: *; +} LIBFWUPD_1.1.1; + +LIBFWUPD_1.1.3 { + global: + fwupd_device_get_install_duration; + 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; + +LIBFWUPD_1.2.5 { + global: + fwupd_device_add_instance_id; + fwupd_device_get_instance_ids; + fwupd_device_has_instance_id; + fwupd_guid_from_string; + fwupd_guid_hash_data; + fwupd_guid_hash_string; + fwupd_guid_is_valid; + fwupd_guid_to_string; + local: *; +} LIBFWUPD_1.2.4; + +LIBFWUPD_1.2.6 { + global: + fwupd_client_activate; + fwupd_client_get_approved_firmware; + fwupd_client_self_sign; + fwupd_client_set_approved_firmware; + fwupd_device_to_json; + fwupd_release_add_flag; + fwupd_release_flag_from_string; + fwupd_release_flag_to_string; + fwupd_release_get_flags; + fwupd_release_has_checksum; + fwupd_release_has_flag; + fwupd_release_remove_flag; + fwupd_release_set_flags; + fwupd_release_to_json; + fwupd_remote_get_approval_required; + local: *; +} LIBFWUPD_1.2.5; + +LIBFWUPD_1.2.7 { + global: + fwupd_release_add_category; + fwupd_release_get_categories; + fwupd_release_has_category; + local: *; +} LIBFWUPD_1.2.6; + +LIBFWUPD_1.2.8 { + global: + fwupd_client_modify_config; + local: *; +} LIBFWUPD_1.2.7; + +LIBFWUPD_1.2.9 { + global: + fwupd_device_get_version_format; + fwupd_device_set_version_format; + fwupd_version_format_from_string; + fwupd_version_format_to_string; + local: *; +} LIBFWUPD_1.2.8; + +LIBFWUPD_1.2.10 { + global: + fwupd_device_array_from_variant; + fwupd_release_array_from_variant; + fwupd_remote_array_from_variant; + local: *; +} LIBFWUPD_1.2.9; diff -Nru fwupd-1.0.9/libfwupd/fwupd-release.c fwupd-1.2.10/libfwupd/fwupd-release.c --- fwupd-1.0.9/libfwupd/fwupd-release.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-release.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2015-2017 Richard Hughes +/* + * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -31,10 +30,14 @@ typedef struct { GPtrArray *checksums; + GPtrArray *categories; GHashTable *metadata; gchar *description; gchar *filename; + gchar *protocol; gchar *homepage; + gchar *details_url; + gchar *source_url; gchar *appstream_id; gchar *license; gchar *name; @@ -44,12 +47,18 @@ gchar *version; gchar *remote_id; guint64 size; - FwupdTrustFlags trust_flags; + guint32 install_duration; + FwupdReleaseFlags flags; + gchar *update_message; } FwupdReleasePrivate; G_DEFINE_TYPE_WITH_PRIVATE (FwupdRelease, fwupd_release, G_TYPE_OBJECT) #define GET_PRIVATE(o) (fwupd_release_get_instance_private (o)) +/* the deprecated fwupd_release_get_trust_flags() function should only + * return the last two bits of the #FwupdReleaseFlags */ +#define FWUPD_RELEASE_TRUST_FLAGS_MASK 0x3 + /** * fwupd_release_get_remote_id: * @release: A #FwupdRelease @@ -159,6 +168,144 @@ } /** + * 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_categories: + * @release: A #FwupdRelease + * + * Gets the release categories. + * + * Returns: (element-type utf8) (transfer none): the categories, which may be empty + * + * Since: 1.2.7 + **/ +GPtrArray * +fwupd_release_get_categories (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->categories; +} + +/** + * fwupd_release_add_category: + * @release: A #FwupdRelease + * @category: the update category, e.g. `X-EmbeddedController` + * + * Adds the update category. + * + * Since: 1.2.7 + **/ +void +fwupd_release_add_category (FwupdRelease *release, const gchar *category) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_return_if_fail (category != NULL); + for (guint i = 0; i < priv->categories->len; i++) { + const gchar *category_tmp = g_ptr_array_index (priv->categories, i); + if (g_strcmp0 (category_tmp, category) == 0) + return; + } + g_ptr_array_add (priv->categories, g_strdup (category)); +} + +/** + * fwupd_release_has_category: + * @release: A #FwupdRelease + * @category: the update category, e.g. `X-EmbeddedController` + * + * Finds out if the release has the update category. + * + * Returns: %TRUE if the release matches + * + * Since: 1.2.7 + **/ +gboolean +fwupd_release_has_category (FwupdRelease *release, const gchar *category) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), FALSE); + g_return_val_if_fail (category != NULL, FALSE); + for (guint i = 0; i < priv->categories->len; i++) { + const gchar *category_tmp = g_ptr_array_index (priv->categories, i); + if (g_strcmp0 (category_tmp, category) == 0) + return TRUE; + } + return FALSE; +} + +/** * fwupd_release_get_checksums: * @release: A #FwupdRelease * @@ -200,6 +347,31 @@ } /** + * fwupd_release_has_checksum: + * @release: A #FwupdRelease + * @checksum: the update checksum + * + * Finds out if the release has the update checksum. + * + * Returns: %TRUE if the release matches + * + * Since: 1.2.6 + **/ +gboolean +fwupd_release_has_checksum (FwupdRelease *release, const gchar *checksum) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), FALSE); + g_return_val_if_fail (checksum != NULL, FALSE); + for (guint i = 0; i < priv->checksums->len; i++) { + const gchar *checksum_tmp = g_ptr_array_index (priv->checksums, i); + if (g_strcmp0 (checksum_tmp, checksum) == 0) + return TRUE; + } + return FALSE; +} + +/** * fwupd_release_get_metadata: * @release: A #FwupdRelease * @@ -357,6 +529,78 @@ } /** + * 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 * @@ -622,7 +866,7 @@ { FwupdReleasePrivate *priv = GET_PRIVATE (release); g_return_val_if_fail (FWUPD_IS_RELEASE (release), 0); - return priv->trust_flags; + return priv->flags & FWUPD_RELEASE_TRUST_FLAGS_MASK; } /** @@ -639,7 +883,133 @@ { FwupdReleasePrivate *priv = GET_PRIVATE (release); g_return_if_fail (FWUPD_IS_RELEASE (release)); - priv->trust_flags = trust_flags; + + /* only overwrite the last two bits of the flags */ + priv->flags &= ~FWUPD_RELEASE_TRUST_FLAGS_MASK; + priv->flags |= trust_flags; +} + +/** + * fwupd_release_get_flags: + * @release: A #FwupdRelease + * + * Gets the release flags. + * + * Returns: the release flags, or 0 if unset + * + * Since: 1.2.6 + **/ +FwupdReleaseFlags +fwupd_release_get_flags (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), 0); + return priv->flags; +} + +/** + * fwupd_release_set_flags: + * @release: A #FwupdRelease + * @flags: the release flags, e.g. %FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD + * + * Sets the release flags. + * + * Since: 1.2.6 + **/ +void +fwupd_release_set_flags (FwupdRelease *release, FwupdReleaseFlags flags) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + priv->flags = flags; +} + +/** + * fwupd_release_add_flag: + * @release: A #FwupdRelease + * @flag: the #FwupdReleaseFlags + * + * Adds a specific release flag to the release. + * + * Since: 1.2.6 + **/ +void +fwupd_release_add_flag (FwupdRelease *release, FwupdReleaseFlags flag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + priv->flags |= flag; +} + +/** + * fwupd_release_remove_flag: + * @release: A #FwupdRelease + * @flag: the #FwupdReleaseFlags + * + * Removes a specific release flag from the release. + * + * Since: 1.2.6 + **/ +void +fwupd_release_remove_flag (FwupdRelease *release, FwupdReleaseFlags flag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + priv->flags &= ~flag; +} + +/** + * fwupd_release_has_flag: + * @release: A #FwupdRelease + * @flag: the #FwupdReleaseFlags + * + * Finds if the release has a specific release flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.2.6 + **/ +gboolean +fwupd_release_has_flag (FwupdRelease *release, FwupdReleaseFlags flag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * 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 * @@ -688,7 +1058,7 @@ g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); /* create an array with all the metadata in */ - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); if (priv->remote_id != NULL) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_REMOTE_ID, @@ -704,6 +1074,11 @@ 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, @@ -729,6 +1104,14 @@ FWUPD_RESULT_KEY_DESCRIPTION, g_variant_new_string (priv->description)); } + if (priv->categories->len > 0) { + g_autofree const gchar **strv = g_new0 (const gchar *, priv->categories->len + 1); + for (guint i = 0; i < priv->categories->len; i++) + strv[i] = (const gchar *) g_ptr_array_index (priv->categories, i); + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_CATEGORIES, + g_variant_new_strv (strv, -1)); + } if (priv->checksums->len > 0) { g_autoptr(GString) str = g_string_new (""); for (guint i = 0; i < priv->checksums->len; i++) { @@ -751,6 +1134,16 @@ 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, @@ -761,16 +1154,21 @@ FWUPD_RESULT_KEY_VENDOR, g_variant_new_string (priv->vendor)); } - if (priv->trust_flags != 0) { + if (priv->flags != 0) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_TRUST_FLAGS, - g_variant_new_uint64 (priv->trust_flags)); + g_variant_new_uint64 (priv->flags)); } if (g_hash_table_size (priv->metadata) > 0) { g_variant_builder_add (&builder, "{sv}", 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); } @@ -790,6 +1188,10 @@ 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; @@ -810,6 +1212,12 @@ fwupd_release_set_description (release, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_CATEGORIES) == 0) { + g_autofree const gchar **strv = g_variant_get_strv (value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_release_add_category (release, strv[i]); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_CHECKSUM) == 0) { const gchar *checksums = g_variant_get_string (value, NULL); g_auto(GStrv) split = g_strsplit (checksums, ",", -1); @@ -825,6 +1233,14 @@ 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; @@ -834,7 +1250,15 @@ return; } if (g_strcmp0 (key, FWUPD_RESULT_KEY_TRUST_FLAGS) == 0) { - fwupd_release_set_trust_flags (release, g_variant_get_uint64 (value)); + fwupd_release_set_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) { @@ -869,23 +1293,127 @@ } static void -fwupd_pad_kv_tfl (GString *str, const gchar *key, FwupdTrustFlags trust_flags) +fwupd_pad_kv_tfl (GString *str, const gchar *key, FwupdReleaseFlags release_flags) { g_autoptr(GString) tmp = g_string_new (""); - for (guint i = 1; i < FWUPD_TRUST_FLAG_LAST; i *= 2) { - if ((trust_flags & i) == 0) + for (guint i = 0; i < 64; i++) { + if ((release_flags & ((guint64) 1 << i)) == 0) continue; g_string_append_printf (tmp, "%s|", - fwupd_trust_flag_to_string (i)); + fwupd_release_flag_to_string ((guint64) 1 << i)); } if (tmp->len == 0) { - g_string_append (tmp, fwupd_trust_flag_to_string (0)); + g_string_append (tmp, fwupd_release_flag_to_string (0)); } else { g_string_truncate (tmp, tmp->len - 1); } 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); +} + +static void +fwupd_release_json_add_string (JsonBuilder *builder, const gchar *key, const gchar *str) +{ + if (str == NULL) + return; + json_builder_set_member_name (builder, key); + json_builder_add_string_value (builder, str); +} + +static void +fwupd_release_json_add_int (JsonBuilder *builder, const gchar *key, guint64 num) +{ + if (num == 0) + return; + json_builder_set_member_name (builder, key); + json_builder_add_int_value (builder, num); +} + +/** + * fwupd_release_to_json: + * @release: A #FwupdRelease + * @builder: A #JsonBuilder + * + * Adds a fwupd release to a JSON builder + * + * Since: 1.2.6 + **/ +void +fwupd_release_to_json (FwupdRelease *release, JsonBuilder *builder) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_autoptr(GList) keys = NULL; + + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_return_if_fail (builder != NULL); + + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_REMOTE_ID, priv->remote_id); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_SUMMARY, priv->summary); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_VERSION, priv->version); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_FILENAME, priv->filename); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_PROTOCOL, priv->protocol); + if (priv->categories->len > 0) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_CATEGORIES); + json_builder_begin_array (builder); + for (guint i = 0; i < priv->categories->len; i++) { + const gchar *tmp = g_ptr_array_index (priv->categories, i); + json_builder_add_string_value (builder, tmp); + } + json_builder_end_array (builder); + } + if (priv->checksums->len > 0) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_CHECKSUM); + json_builder_begin_array (builder); + for (guint i = 0; i < priv->checksums->len; i++) { + const gchar *checksum = g_ptr_array_index (priv->checksums, i); + json_builder_add_string_value (builder, checksum); + } + json_builder_end_array (builder); + } + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_LICENSE, priv->license); + fwupd_release_json_add_int (builder, FWUPD_RESULT_KEY_SIZE, priv->size); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_URI, priv->uri); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_HOMEPAGE, priv->homepage); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_DETAILS_URL, priv->details_url); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_SOURCE_URL, priv->source_url); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_VENDOR, priv->vendor); + if (priv->flags != FWUPD_RELEASE_FLAG_NONE) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_FLAGS); + json_builder_begin_array (builder); + for (guint i = 0; i < 64; i++) { + const gchar *tmp; + if ((priv->flags & ((guint64) 1 << i)) == 0) + continue; + tmp = fwupd_release_flag_to_string ((guint64) 1 << i); + json_builder_add_string_value (builder, tmp); + } + json_builder_end_array (builder); + } + fwupd_release_json_add_int (builder, FWUPD_RESULT_KEY_INSTALL_DURATION, priv->install_duration); + fwupd_release_json_add_string (builder, 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) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup (priv->metadata, key); + fwupd_release_json_add_string (builder, key, value); + } +} + /** * fwupd_release_to_string: * @release: A #FwupdRelease @@ -912,6 +1440,11 @@ 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->categories->len; i++) { + const gchar *tmp = g_ptr_array_index (priv->categories, i); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_CATEGORIES, tmp); + } for (guint i = 0; i < priv->checksums->len; i++) { const gchar *checksum = g_ptr_array_index (priv->checksums, i); g_autofree gchar *checksum_display = fwupd_checksum_format_for_display (checksum); @@ -921,9 +1454,13 @@ 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_tfl (str, FWUPD_RESULT_KEY_FLAGS, priv->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) { @@ -946,6 +1483,7 @@ fwupd_release_init (FwupdRelease *release) { FwupdReleasePrivate *priv = GET_PRIVATE (release); + priv->categories = g_ptr_array_new_with_free_func (g_free); priv->checksums = g_ptr_array_new_with_free_func (g_free); priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } @@ -958,15 +1496,20 @@ 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->categories); g_ptr_array_unref (priv->checksums); g_hash_table_unref (priv->metadata); @@ -986,30 +1529,30 @@ /** * fwupd_release_from_variant: - * @data: a #GVariant + * @value: a #GVariant * * Creates a new release using packed data. * - * Returns: (transfer full): a new #FwupdRelease, or %NULL if @data was invalid + * Returns: (transfer full): a new #FwupdRelease, or %NULL if @value was invalid * * Since: 1.0.0 **/ FwupdRelease * -fwupd_release_from_variant (GVariant *data) +fwupd_release_from_variant (GVariant *value) { FwupdRelease *rel = NULL; const gchar *type_string; g_autoptr(GVariantIter) iter = NULL; /* format from GetDetails */ - type_string = g_variant_get_type_string (data); + type_string = g_variant_get_type_string (value); if (g_strcmp0 (type_string, "(a{sv})") == 0) { rel = fwupd_release_new (); - g_variant_get (data, "(a{sv})", &iter); + g_variant_get (value, "(a{sv})", &iter); fwupd_release_set_from_variant_iter (rel, iter); } else if (g_strcmp0 (type_string, "a{sv}") == 0) { rel = fwupd_release_new (); - g_variant_get (data, "a{sv}", &iter); + g_variant_get (value, "a{sv}", &iter); fwupd_release_set_from_variant_iter (rel, iter); } else { g_warning ("type %s not known", type_string); @@ -1018,6 +1561,38 @@ } /** + * fwupd_release_array_from_variant: + * @value: a #GVariant + * + * Creates an array of new releases using packed data. + * + * Returns: (transfer container) (element-type FwupdRelease): releases, or %NULL if @value was invalid + * + * Since: 1.2.10 + **/ +GPtrArray * +fwupd_release_array_from_variant (GVariant *value) +{ + GPtrArray *array = NULL; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + + array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + untuple = g_variant_get_child_value (value, 0); + sz = g_variant_n_children (untuple); + for (guint i = 0; i < sz; i++) { + FwupdRelease *rel; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value (untuple, i); + rel = fwupd_release_from_variant (data); + if (rel == NULL) + continue; + g_ptr_array_add (array, rel); + } + return array; +} + +/** * fwupd_release_new: * * Creates a new release. diff -Nru fwupd-1.0.9/libfwupd/fwupd-release.h fwupd-1.2.10/libfwupd/fwupd-release.h --- fwupd-1.0.9/libfwupd/fwupd-release.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-release.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2015-2017 Richard Hughes +/* + * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_RELEASE_H -#define __FWUPD_RELEASE_H +#pragma once #include @@ -39,9 +37,16 @@ const gchar *fwupd_release_get_uri (FwupdRelease *release); void fwupd_release_set_uri (FwupdRelease *release, const gchar *uri); +GPtrArray *fwupd_release_get_categories (FwupdRelease *release); +void fwupd_release_add_category (FwupdRelease *release, + const gchar *category); +gboolean fwupd_release_has_category (FwupdRelease *release, + const gchar *category); GPtrArray *fwupd_release_get_checksums (FwupdRelease *release); void fwupd_release_add_checksum (FwupdRelease *release, const gchar *checksum); +gboolean fwupd_release_has_checksum (FwupdRelease *release, + const gchar *checksum); GHashTable *fwupd_release_get_metadata (FwupdRelease *release); void fwupd_release_add_metadata (FwupdRelease *release, @@ -55,6 +60,9 @@ 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); @@ -76,17 +84,40 @@ 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); const gchar *fwupd_release_get_license (FwupdRelease *release); void fwupd_release_set_license (FwupdRelease *release, const gchar *license); -FwupdTrustFlags fwupd_release_get_trust_flags (FwupdRelease *release); +FwupdTrustFlags fwupd_release_get_trust_flags (FwupdRelease *release) +G_DEPRECATED_FOR(fwupd_release_get_flags); void fwupd_release_set_trust_flags (FwupdRelease *release, - FwupdTrustFlags trust_flags); + FwupdTrustFlags trust_flags) +G_DEPRECATED_FOR(fwupd_release_set_flags); +FwupdReleaseFlags fwupd_release_get_flags (FwupdRelease *release); +void fwupd_release_set_flags (FwupdRelease *release, + FwupdReleaseFlags flags); +void fwupd_release_add_flag (FwupdRelease *release, + FwupdReleaseFlags flag); +void fwupd_release_remove_flag (FwupdRelease *release, + FwupdReleaseFlags flag); +gboolean fwupd_release_has_flag (FwupdRelease *release, + FwupdReleaseFlags flag); +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 - -#endif /* __FWUPD_RELEASE_H */ +FwupdRelease *fwupd_release_from_variant (GVariant *value); +GPtrArray *fwupd_release_array_from_variant (GVariant *value); +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-release-private.h fwupd-1.2.10/libfwupd/fwupd-release-private.h --- fwupd-1.0.9/libfwupd/fwupd-release-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-release-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,23 +1,21 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_RELEASE_PRIVATE_H -#define __FWUPD_RELEASE_PRIVATE_H +#pragma once #include +#include #include "fwupd-release.h" G_BEGIN_DECLS -FwupdRelease *fwupd_release_from_variant (GVariant *data); GVariant *fwupd_release_to_variant (FwupdRelease *release); +void fwupd_release_to_json (FwupdRelease *release, + JsonBuilder *builder); G_END_DECLS -#endif /* __FWUPD_RELEASE_PRIVATE_H */ - diff -Nru fwupd-1.0.9/libfwupd/fwupd-remote.c fwupd-1.2.10/libfwupd/fwupd-remote.c --- fwupd-1.0.9/libfwupd/fwupd-remote.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-remote.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -43,6 +42,7 @@ gchar *filename_cache_sig; gchar *filename_source; gboolean enabled; + gboolean approval_required; gint priority; guint64 mtime; gchar **order_after; @@ -53,6 +53,7 @@ PROP_0, PROP_ID, PROP_ENABLED, + PROP_APPROVAL_REQUIRED, PROP_LAST }; @@ -276,6 +277,8 @@ 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; } @@ -296,6 +299,8 @@ return "download"; if (kind == FWUPD_REMOTE_KIND_LOCAL) return "local"; + if (kind == FWUPD_REMOTE_KIND_DIRECTORY) + return "directory"; return NULL; } @@ -385,7 +390,14 @@ 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; @@ -400,6 +412,7 @@ /* extract data */ priv->enabled = g_key_file_get_boolean (kf, group, "Enabled", NULL); + priv->approval_required = g_key_file_get_boolean (kf, group, "ApprovalRequired", NULL); priv->title = g_key_file_get_string (kf, group, "Title", NULL); /* reporting is optional */ @@ -445,14 +458,6 @@ 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)) { @@ -474,6 +479,25 @@ 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) @@ -890,6 +914,25 @@ } /** + * fwupd_remote_get_approval_required: + * @self: A #FwupdRemote + * + * Gets if firmware from the remote should be checked against the list + * of a approved checksums. + * + * Returns: a #TRUE if the remote is restricted + * + * Since: 1.2.6 + **/ +gboolean +fwupd_remote_get_approval_required (FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_REMOTE (self), FALSE); + return priv->approval_required; +} + +/** * fwupd_remote_get_id: * @self: A #FwupdRemote * @@ -948,6 +991,8 @@ fwupd_remote_set_checksum (self, g_variant_get_string (value, NULL)); } else if (g_strcmp0 (key, "Enabled") == 0) { priv->enabled = g_variant_get_boolean (value); + } else if (g_strcmp0 (key, "ApprovalRequired") == 0) { + priv->approval_required = g_variant_get_boolean (value); } else if (g_strcmp0 (key, "Priority") == 0) { priv->priority = g_variant_get_int32 (value); } else if (g_strcmp0 (key, "ModificationTime") == 0) { @@ -977,7 +1022,7 @@ g_return_val_if_fail (FWUPD_IS_REMOTE (self), NULL); /* create an array with all the metadata in */ - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); if (priv->id != NULL) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_REMOTE_ID, g_variant_new_string (priv->id)); @@ -1040,6 +1085,8 @@ } g_variant_builder_add (&builder, "{sv}", "Enabled", g_variant_new_boolean (priv->enabled)); + g_variant_builder_add (&builder, "{sv}", "ApprovalRequired", + g_variant_new_boolean (priv->approval_required)); return g_variant_new ("a{sv}", &builder); } @@ -1054,6 +1101,9 @@ case PROP_ENABLED: g_value_set_boolean (value, priv->enabled); break; + case PROP_APPROVAL_REQUIRED: + g_value_set_boolean (value, priv->approval_required); + break; case PROP_ID: g_value_set_string (value, priv->id); break; @@ -1074,6 +1124,9 @@ case PROP_ENABLED: priv->enabled = g_value_get_boolean (value); break; + case PROP_APPROVAL_REQUIRED: + priv->approval_required = g_value_get_boolean (value); + break; case PROP_ID: fwupd_remote_set_id (self, g_value_get_string (value)); break; @@ -1100,7 +1153,7 @@ * 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); /** @@ -1111,8 +1164,20 @@ * 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); + + /** + * FwupdRemote:approval-required: + * + * If firmware from the remote should be checked against the system + * list of approved firmware. + * + * Since: 1.2.6 + */ + pspec = g_param_spec_boolean ("approval-required", NULL, NULL, + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_APPROVAL_REQUIRED, pspec); } static void @@ -1147,30 +1212,30 @@ /** * fwupd_remote_from_variant: - * @data: a #GVariant + * @value: a #GVariant * * Creates a new remote using packed data. * - * Returns: (transfer full): a new #FwupdRemote, or %NULL if @data was invalid + * Returns: (transfer full): a new #FwupdRemote, or %NULL if @value was invalid * * Since: 1.0.0 **/ FwupdRemote * -fwupd_remote_from_variant (GVariant *data) +fwupd_remote_from_variant (GVariant *value) { FwupdRemote *rel = NULL; const gchar *type_string; g_autoptr(GVariantIter) iter = NULL; - type_string = g_variant_get_type_string (data); + type_string = g_variant_get_type_string (value); if (g_strcmp0 (type_string, "(a{sv})") == 0) { rel = fwupd_remote_new (); - g_variant_get (data, "(a{sv})", &iter); + g_variant_get (value, "(a{sv})", &iter); fwupd_remote_set_from_variant_iter (rel, iter); fwupd_remote_set_from_variant_iter (rel, iter); } else if (g_strcmp0 (type_string, "a{sv}") == 0) { rel = fwupd_remote_new (); - g_variant_get (data, "a{sv}", &iter); + g_variant_get (value, "a{sv}", &iter); fwupd_remote_set_from_variant_iter (rel, iter); } else { g_warning ("type %s not known", type_string); @@ -1179,6 +1244,35 @@ } /** + * fwupd_remote_array_from_variant: + * @value: a #GVariant + * + * Creates an array of new devices using packed data. + * + * Returns: (transfer container) (element-type FwupdRemote): remotes, or %NULL if @value was invalid + * + * Since: 1.2.10 + **/ +GPtrArray * +fwupd_remote_array_from_variant (GVariant *value) +{ + GPtrArray *remotes = NULL; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + + remotes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + untuple = g_variant_get_child_value (value, 0); + sz = g_variant_n_children (untuple); + for (guint i = 0; i < sz; i++) { + g_autoptr(GVariant) data = g_variant_get_child_value (untuple, i); + FwupdRemote *remote = fwupd_remote_from_variant (data); + g_ptr_array_add (remotes, remote); + } + + return remotes; +} + +/** * fwupd_remote_new: * * Creates a new fwupd remote. diff -Nru fwupd-1.0.9/libfwupd/fwupd-remote.h fwupd-1.2.10/libfwupd/fwupd-remote.h --- fwupd-1.0.9/libfwupd/fwupd-remote.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-remote.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_REMOTE_H -#define __FWUPD_REMOTE_H +#pragma once #include "fwupd-enums.h" @@ -33,6 +31,7 @@ * @FWUPD_REMOTE_KIND_UNKNOWN: Unknown kind * @FWUPD_REMOTE_KIND_DOWNLOAD: Requires files to be downloaded * @FWUPD_REMOTE_KIND_LOCAL: Reads files from the local machine + * @FWUPD_REMOTE_KIND_DIRECTORY: Reads directory from the local machine * * The kind of remote. **/ @@ -40,6 +39,7 @@ 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; @@ -62,6 +62,7 @@ const gchar *fwupd_remote_get_metadata_uri (FwupdRemote *self); const gchar *fwupd_remote_get_metadata_uri_sig (FwupdRemote *self); gboolean fwupd_remote_get_enabled (FwupdRemote *self); +gboolean fwupd_remote_get_approval_required (FwupdRemote *self); gint fwupd_remote_get_priority (FwupdRemote *self); guint64 fwupd_remote_get_age (FwupdRemote *self); FwupdRemoteKind fwupd_remote_get_kind (FwupdRemote *self); @@ -70,7 +71,7 @@ const gchar *url, GError **error); -G_END_DECLS - -#endif /* __FWUPD_REMOTE_H */ +FwupdRemote *fwupd_remote_from_variant (GVariant *value); +GPtrArray *fwupd_remote_array_from_variant (GVariant *value); +G_END_DECLS diff -Nru fwupd-1.0.9/libfwupd/fwupd-remote-private.h fwupd-1.2.10/libfwupd/fwupd-remote-private.h --- fwupd-1.0.9/libfwupd/fwupd-remote-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-remote-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,18 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FWUPD_REMOTE_PRIVATE_H -#define __FWUPD_REMOTE_PRIVATE_H +#pragma once #include "fwupd-remote.h" G_BEGIN_DECLS -FwupdRemote *fwupd_remote_from_variant (GVariant *data); GVariant *fwupd_remote_to_variant (FwupdRemote *self); gboolean fwupd_remote_load_from_filename (FwupdRemote *self, const gchar *filename, @@ -28,6 +25,3 @@ gchar **fwupd_remote_get_order_before (FwupdRemote *self); G_END_DECLS - -#endif /* __FWUPD_REMOTE_PRIVATE_H */ - diff -Nru fwupd-1.0.9/libfwupd/fwupd-self-test.c fwupd-1.2.10/libfwupd/fwupd-self-test.c --- fwupd-1.0.9/libfwupd/fwupd-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -9,12 +8,12 @@ #include #include -#include #include "fwupd-client.h" #include "fwupd-common.h" #include "fwupd-enums.h" #include "fwupd-error.h" +#include "fwupd-device-private.h" #include "fwupd-release-private.h" #include "fwupd-remote-private.h" @@ -45,6 +44,60 @@ return FALSE; } +/* https://gitlab.gnome.org/GNOME/glib/issues/225 */ +static guint +_g_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; +} + static void fwupd_enums_func (void) { @@ -69,6 +122,11 @@ g_assert_cmpstr (tmp, !=, NULL); g_assert_cmpint (fwupd_trust_flag_from_string (tmp), ==, i); } + for (guint i = 1; i < FWUPD_VERSION_FORMAT_LAST; i++) { + const gchar *tmp = fwupd_version_format_to_string (i); + g_assert_cmpstr (tmp, !=, NULL); + g_assert_cmpint (fwupd_version_format_from_string (tmp), ==, i); + } /* bitfield */ for (guint64 i = 1; i < FWUPD_DEVICE_FLAG_UNKNOWN; i *= 2) { @@ -173,7 +231,7 @@ g_autoptr(GError) error = NULL; remote = fwupd_remote_new (); - fn = g_build_filename (FU_SELF_TEST_REMOTES_DIR, "remotes.d", "fwupd.conf", NULL); + fn = g_build_filename (FU_LOCAL_REMOTE_DIR, "dell-esrt.conf", NULL); ret = fwupd_remote_load_from_filename (remote, fn, NULL, &error); g_assert_no_error (error); g_assert (ret); @@ -183,8 +241,8 @@ g_assert (fwupd_remote_get_metadata_uri (remote) == NULL); g_assert (fwupd_remote_get_metadata_uri_sig (remote) == NULL); g_assert (fwupd_remote_get_report_uri (remote) == NULL); - g_assert_cmpstr (fwupd_remote_get_title (remote), ==, "Core"); - g_assert_cmpstr (fwupd_remote_get_filename_cache (remote), ==, "@datadir@/fwupd/remotes.d/fwupd/metadata.xml"); + g_assert_cmpstr (fwupd_remote_get_title (remote), ==, "Enable UEFI capsule updates on Dell systems"); + g_assert_cmpstr (fwupd_remote_get_filename_cache (remote), ==, "@datadir@/fwupd/remotes.d/dell-esrt/metadata.xml"); g_assert_cmpstr (fwupd_remote_get_filename_cache_sig (remote), ==, NULL); g_assert_cmpstr (fwupd_remote_get_checksum (remote), ==, NULL); } @@ -209,10 +267,15 @@ fwupd_device_func (void) { gboolean ret; + g_autofree gchar *data = NULL; g_autofree gchar *str = NULL; g_autoptr(FwupdDevice) dev = NULL; g_autoptr(FwupdRelease) rel = NULL; g_autoptr(GError) error = NULL; + g_autoptr(GString) str_ascii = NULL; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; /* create dummy object */ dev = fwupd_device_new (); @@ -228,7 +291,7 @@ fwupd_device_add_icon (dev, "input-mouse"); fwupd_device_add_flag (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); rel = fwupd_release_new (); - fwupd_release_set_trust_flags (rel, FWUPD_TRUST_FLAG_PAYLOAD); + fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD); fwupd_release_add_checksum (rel, "deadbeef"); fwupd_release_set_description (rel, "

Hi there!

"); fwupd_release_set_filename (rel, "firmware.bin"); @@ -245,7 +308,11 @@ g_assert (fwupd_device_has_guid (dev, "00000000-0000-0000-0000-000000000000")); g_assert (!fwupd_device_has_guid (dev, "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")); - ret = fu_test_compare_lines (str, + /* convert the new non-breaking space back into a normal space: + * https://gitlab.gnome.org/GNOME/glib/commit/76af5dabb4a25956a6c41a75c0c7feeee74496da */ + str_ascii = g_string_new (str); + _g_string_replace (str_ascii, " ", " "); + ret = fu_test_compare_lines (str_ascii->str, "ColorHug2\n" " DeviceId: USB:foo\n" " Guid: 2082b5e0-7a64-478a-b1b2-e3404fab6dad\n" @@ -264,7 +331,59 @@ " Checksum: SHA1(deadbeef)\n" " Size: 1.0 kB\n" " Uri: http://foo.com\n" - " TrustFlags: payload\n", &error); + " Flags: trusted-payload\n", &error); + g_assert_no_error (error); + g_assert (ret); + + /* export to json */ + builder = json_builder_new (); + json_builder_begin_object (builder); + fwupd_device_to_json (dev, builder); + json_builder_end_object (builder); + json_root = json_builder_get_root (builder); + json_generator = json_generator_new (); + json_generator_set_pretty (json_generator, TRUE); + json_generator_set_root (json_generator, json_root); + data = json_generator_to_data (json_generator, NULL); + g_assert_nonnull (data); + ret = fu_test_compare_lines (data, + "{\n" + " \"Name\" : \"ColorHug2\",\n" + " \"DeviceId\" : \"USB:foo\",\n" + " \"Guid\" : [\n" + " \"2082b5e0-7a64-478a-b1b2-e3404fab6dad\",\n" + " \"00000000-0000-0000-0000-000000000000\"\n" + " ],\n" + " \"Flags\" : [\n" + " \"updatable\",\n" + " \"require-ac\"\n" + " ],\n" + " \"Checksums\" : [\n" + " \"beefdead\"\n" + " ],\n" + " \"Icons\" : [\n" + " \"input-gaming\",\n" + " \"input-mouse\"\n" + " ],\n" + " \"Created\" : 1,\n" + " \"Modified\" : 86400,\n" + " \"Releases\" : [\n" + " {\n" + " \"AppstreamId\" : \"org.dave.ColorHug.firmware\",\n" + " \"Description\" : \"

Hi there!

\",\n" + " \"Version\" : \"1.2.3\",\n" + " \"Filename\" : \"firmware.bin\",\n" + " \"Checksum\" : [\n" + " \"deadbeef\"\n" + " ],\n" + " \"Size\" : 1024,\n" + " \"Uri\" : \"http://foo.com\",\n" + " \"Flags\" : [\n" + " \"trusted-payload\"\n" + " ]\n" + " }\n" + " ]\n" + "}", &error); g_assert_no_error (error); g_assert (ret); } @@ -288,7 +407,7 @@ g_test_skip ("no enabled fwupd daemon"); return; } - if (as_utils_vercmp (fwupd_client_get_daemon_version (client), "1.0.0") < 0) { + if (!g_str_has_prefix (fwupd_client_get_daemon_version (client), "1.")) { g_test_skip ("running fwupd is too old"); return; } @@ -337,7 +456,7 @@ g_test_skip ("no enabled fwupd daemon"); return; } - if (as_utils_vercmp (fwupd_client_get_daemon_version (client), "1.0.0") < 0) { + if (!g_str_has_prefix (fwupd_client_get_daemon_version (client), "1.")) { g_test_skip ("running fwupd is too old"); return; } @@ -403,6 +522,58 @@ g_assert_cmpstr (mhash2, !=, mhash1); } +static void +fwupd_common_guid_func (void) +{ + g_autofree gchar *guid1 = NULL; + g_autofree gchar *guid2 = NULL; + g_autofree gchar *guid_be = NULL; + g_autofree gchar *guid_me = NULL; + fwupd_guid_t buf = { 0x0 }; + gboolean ret; + g_autoptr(GError) error = NULL; + + /* invalid */ + g_assert (!fwupd_guid_is_valid (NULL)); + g_assert (!fwupd_guid_is_valid ("")); + g_assert (!fwupd_guid_is_valid ("1ff60ab2-3905-06a1-b476")); + g_assert (!fwupd_guid_is_valid ("1ff60ab2-XXXX-XXXX-XXXX-0371f00c9e9b")); + g_assert (!fwupd_guid_is_valid (" 1ff60ab2-3905-06a1-b476-0371f00c9e9b")); + g_assert (!fwupd_guid_is_valid ("00000000-0000-0000-0000-000000000000")); + + /* valid */ + g_assert (fwupd_guid_is_valid ("1ff60ab2-3905-06a1-b476-0371f00c9e9b")); + + /* make valid */ + guid1 = fwupd_guid_hash_string ("python.org"); + g_assert_cmpstr (guid1, ==, "886313e1-3b8a-5372-9b90-0c9aee199e5d"); + + guid2 = fwupd_guid_hash_string ("8086:0406"); + g_assert_cmpstr (guid2, ==, "1fbd1f2c-80f4-5d7c-a6ad-35c7b9bd5486"); + + /* round-trip BE */ + ret = fwupd_guid_from_string ("00112233-4455-6677-8899-aabbccddeeff", &buf, + FWUPD_GUID_FLAG_NONE, &error); + g_assert_true (ret); + g_assert_no_error (error); + g_assert (memcmp (buf, "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff", sizeof(buf)) == 0); + guid_be = fwupd_guid_to_string ((const fwupd_guid_t *) &buf, FWUPD_GUID_FLAG_NONE); + g_assert_cmpstr (guid_be, ==, "00112233-4455-6677-8899-aabbccddeeff"); + + /* round-trip mixed encoding */ + ret = fwupd_guid_from_string ("00112233-4455-6677-8899-aabbccddeeff", &buf, + FWUPD_GUID_FLAG_MIXED_ENDIAN, &error); + g_assert_true (ret); + g_assert_no_error (error); + g_assert (memcmp (buf, "\x33\x22\x11\x00\x55\x44\x77\x66\x88\x99\xaa\xbb\xcc\xdd\xee\xff", sizeof(buf)) == 0); + guid_me = fwupd_guid_to_string ((const fwupd_guid_t *) &buf, FWUPD_GUID_FLAG_MIXED_ENDIAN); + g_assert_cmpstr (guid_me, ==, "00112233-4455-6677-8899-aabbccddeeff"); + + /* check failure */ + g_assert_false (fwupd_guid_from_string ("001122334455-6677-8899-aabbccddeeff", NULL, 0, NULL)); + g_assert_false (fwupd_guid_from_string ("0112233-4455-6677-8899-aabbccddeeff", NULL, 0, NULL)); +} + int main (int argc, char **argv) { @@ -415,6 +586,7 @@ /* tests go here */ g_test_add_func ("/fwupd/enums", fwupd_enums_func); g_test_add_func ("/fwupd/common{machine-hash}", fwupd_common_machine_hash_func); + g_test_add_func ("/fwupd/common{guid}", fwupd_common_guid_func); g_test_add_func ("/fwupd/release", fwupd_release_func); g_test_add_func ("/fwupd/device", fwupd_device_func); g_test_add_func ("/fwupd/remote{download}", fwupd_remote_download_func); diff -Nru fwupd-1.0.9/libfwupd/fwupd-version.h.in fwupd-1.2.10/libfwupd/fwupd-version.h.in --- fwupd-1.0.9/libfwupd/fwupd-version.h.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/fwupd-version.h.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,9 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ + +#pragma once + /** * SECTION:fwupd-version * @short_description: Obtains the version for the installed fwupd @@ -16,9 +18,6 @@ #error "Only can be included directly." #endif -#ifndef __FWUPD_VERSION_H -#define __FWUPD_VERSION_H - /** * FWUPD_MAJOR_VERSION: * @@ -60,5 +59,3 @@ (FWUPD_MAJOR_VERSION == (major) && FWUPD_MINOR_VERSION > (minor)) || \ (FWUPD_MAJOR_VERSION == (major) && FWUPD_MINOR_VERSION == (minor) && \ FWUPD_MICRO_VERSION >= (micro))) - -#endif /* __FWUPD_VERSION_H */ diff -Nru fwupd-1.0.9/libfwupd/generate-version-script.py fwupd-1.2.10/libfwupd/generate-version-script.py --- fwupd-1.0.9/libfwupd/generate-version-script.py 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/generate-version-script.py 2019-07-15 18:25:54.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/python3 # pylint: disable=invalid-name,missing-docstring # # Copyright (C) 2017 Richard Hughes @@ -8,6 +8,8 @@ import sys import xml.etree.ElementTree as ET +from pkg_resources import parse_version + XMLNS = '{http://www.gtk.org/introspection/core/1.0}' XMLNS_C = '{http://www.gtk.org/introspection/c/1.0}' @@ -32,7 +34,6 @@ if 'version' not in node.attrib: print('No version for', identifier) sys.exit(1) - return version = node.attrib['version'] if version not in self.releases: self.releases[version] = [] @@ -40,36 +41,43 @@ release.append(identifier) return version + def _add_cls(self, cls): + + # add all class functions + for node in cls.findall(XMLNS + 'function'): + self._add_node(node) + + # add the constructor + for node in cls.findall(XMLNS + 'constructor'): + self._add_node(node) + + # choose the lowest version method for the _get_type symbol + version_lowest = None + if '{http://www.gtk.org/introspection/glib/1.0}get-type' not in cls.attrib: + return + type_name = cls.attrib['{http://www.gtk.org/introspection/glib/1.0}get-type'] + + # add all class methods + for node in cls.findall(XMLNS + 'method'): + version_tmp = self._add_node(node) + if version_tmp: + if not version_lowest or version_tmp < version_lowest: + version_lowest = version_tmp + + # finally add the get_type symbol + if version_lowest: + self.releases[version_lowest].append(type_name) + def import_gir(self, filename): tree = ET.parse(filename) root = tree.getroot() for ns in root.findall(XMLNS + 'namespace'): for node in ns.findall(XMLNS + 'function'): self._add_node(node) + for cls in ns.findall(XMLNS + 'record'): + self._add_cls(cls) for cls in ns.findall(XMLNS + 'class'): - - # add all class functions - for node in cls.findall(XMLNS + 'function'): - self._add_node(node) - - # add the constructor - for node in cls.findall(XMLNS + 'constructor'): - self._add_node(node) - - # choose the lowest version method for the _get_type symbol - version_lowest = None - type_name = cls.attrib['{http://www.gtk.org/introspection/glib/1.0}get-type'] - - # add all class methods - for node in cls.findall(XMLNS + 'method'): - version_tmp = self._add_node(node) - if version_tmp: - if not version_lowest or version_tmp < version_lowest: - version_lowest = version_tmp - - # finally add the get_type symbol - if version_lowest: - self.releases[version_lowest].append(type_name) + self._add_cls(cls) def render(self): @@ -81,7 +89,7 @@ # output the version data to a file verout = '# generated automatically, do not edit!\n' oldversion = None - for version in sorted(versions): + for version in sorted(versions, key=parse_version): symbols = sorted(self.releases[version]) verout += '\n%s_%s {\n' % (self.library_name, version) verout += ' global:\n' diff -Nru fwupd-1.0.9/libfwupd/meson.build fwupd-1.2.10/libfwupd/meson.build --- fwupd-1.0.9/libfwupd/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/libfwupd/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -96,7 +96,7 @@ symbol_prefix : 'fwupd', identifier_prefix : 'Fwupd', export_packages : 'fwupd', - extra_args : '--c-include=fwupd.h', # This can be replaced with header : 'fwupd.h' once can depend on Meson 0.43.0 + header : 'fwupd.h', dependencies : [ giounix, soup, @@ -122,7 +122,7 @@ # 2. The map file is required to build the lib that the GIR is built from # # To avoid the circular dep, and to ensure we don't change exported API - # accidentaly actually check in a version of the version script to git. + # accidentally actually check in a version of the version script to git. mapfile_target = custom_target('mapfile', input: gir[0], output: 'fwupd.map', @@ -145,6 +145,7 @@ if get_option('tests') testdatadir = join_paths(meson.source_root(), 'data') + localremotetestdir = join_paths(meson.source_root(), 'plugins', 'dell-esrt') e = executable( 'fwupd-self-test', sources : [ @@ -154,9 +155,9 @@ include_directories('..'), ], dependencies : [ - appstream_glib, gio, soup, + libjsonglib, ], link_with : fwupd, c_args : [ @@ -164,6 +165,7 @@ '-DLOCALSTATEDIR="' + localstatedir + '"', '-DTESTDATADIR="' + testdatadir + '"', '-DFU_SELF_TEST_REMOTES_DIR="' + testdatadir + '"', + '-DFU_LOCAL_REMOTE_DIR="' + localremotetestdir + '"', ], ) test('fwupd-self-test', e) diff -Nru fwupd-1.0.9/meson.build fwupd-1.2.10/meson.build --- fwupd-1.0.9/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,7 +1,7 @@ project('fwupd', 'c', - version : '1.0.9', + version : '1.2.10', license : 'LGPL-2.1+', - meson_version : '>=0.43.0', + meson_version : '>=0.47.0', default_options : ['warning_level=2', 'c_std=c99'], ) @@ -48,7 +48,7 @@ lt_age = '0' lt_version = '@0@.@1@.@2@'.format(lt_current, lt_age, lt_revision) -# get suported warning flags +# get supported warning flags warning_flags = [ '-fstack-protector-strong', '-Waggregate-return', @@ -77,7 +77,7 @@ '-Wmissing-prototypes', '-Wnested-externs', '-Wno-cast-function-type', - '-Wno-error=cpp', + '-Wno-address-of-packed-member', # incompatible with g_autoptr() '-Wno-unknown-pragmas', '-Wno-discarded-qualifiers', '-Wno-missing-field-initializers', @@ -100,6 +100,7 @@ '-Wuninitialized', '-Wunused-but-set-variable', '-Wunused-variable', + '-Wvla', '-Wwrite-strings' ] cc = meson.get_compiler('c') @@ -110,10 +111,11 @@ global_link_args = [] test_link_args = [ '-Wl,-z,relro', + '-Wl,-z,defs', '-Wl,-z,now', ] foreach arg: test_link_args - if cc.has_argument(arg) + if cc.has_link_argument(arg) global_link_args += arg endif endforeach @@ -128,17 +130,32 @@ # do not use deprecated symbols or defines internally add_project_arguments('-DFWUPD_DISABLE_DEPRECATED', language : 'c') -# needed for some things on CentOS -add_project_arguments('-D_GNU_SOURCE', language : 'c') +# needed for symlink() and BYTE_ORDER +add_project_arguments('-D_BSD_SOURCE', language : 'c') +add_project_arguments('-D_XOPEN_SOURCE=700', language : 'c') + +prefix = get_option('prefix') + +bindir = join_paths(prefix, get_option('bindir')) +libdir = join_paths(prefix, get_option('libdir')) +datadir = join_paths(prefix, get_option('datadir')) +libexecdir = join_paths(prefix, get_option('libexecdir')) +sysconfdir = join_paths(prefix, get_option('sysconfdir')) +localstatedir = join_paths(prefix, get_option('localstatedir')) +mandir = join_paths(prefix, get_option('mandir')) +localedir = join_paths(prefix, get_option('localedir')) gio = dependency('gio-2.0', version : '>= 2.45.8') +if gio.version().version_compare ('>= 2.55.0') + conf.set('HAVE_GIO_2_55_0', '1') +endif gmodule = dependency('gmodule-2.0') giounix = dependency('gio-unix-2.0', version : '>= 2.45.8') gudev = dependency('gudev-1.0') if gudev.version().version_compare('>= 232') conf.set('HAVE_GUDEV_232', '1') endif -appstream_glib = dependency('appstream-glib', version : '>= 0.7.4') +libxmlb = dependency('xmlb', version : '>= 0.1.7', fallback : ['libxmlb', 'libxmlb_dep']) gusb = dependency('gusb', version : '>= 0.2.9') sqlite = dependency('sqlite3') libarchive = dependency('libarchive') @@ -150,6 +167,7 @@ if polkit.version().version_compare('>= 0.114') conf.set('HAVE_POLKIT_0_114', '1') endif + conf.set_quoted ('POLKIT_ACTIONDIR', polkit.get_pkgconfig_variable('actiondir')) udevdir = get_option('udevdir') if udevdir == '' udev = dependency('udev') @@ -158,6 +176,9 @@ endif if get_option('pkcs7') gnutls = dependency('gnutls', version : '>= 3.4.4.1') + if gnutls.version().version_compare('>= 3.6.0') + conf.set('HAVE_GNUTLS_3_6_0', '1') + endif conf.set('ENABLE_PKCS7', '1') endif if get_option('gpg') @@ -166,7 +187,6 @@ conf.set('ENABLE_GPG', '1') endif libm = cc.find_library('m', required: false) -uuid = dependency('uuid') libgcab = dependency('libgcab-1.0') if libgcab.version().version_compare('>= 0.8') conf.set('HAVE_GCAB_0_8', '1') @@ -176,43 +196,77 @@ endif gcab = find_program('gcab', required : true) bashcomp = dependency('bash-completion', required: false) - -if get_option('plugin_uefi_labels') - cairo = dependency('cairo') - fontconfig = cc.find_library('fontconfig') - freetype = cc.find_library('freetype') - r = run_command('po/test-deps') - if r.returncode() != 0 - error(r.stdout()) - endif -endif +python3 = find_program('python3') if valgrind.found() conf.set('HAVE_VALGRIND', '1') endif +if get_option('plugin_redfish') + efivar = dependency('efivar') +endif + if get_option('plugin_altos') libelf = dependency('libelf') endif if get_option('plugin_uefi') - fwup = dependency('fwup', version : '>= 10') - if fwup.version().version_compare('>= 11') - conf.set('HAVE_FWUP_GET_ESP_MOUNTPOINT', '1') + cairo = dependency('cairo') + fontconfig = cc.find_library('fontconfig') + freetype = cc.find_library('freetype') + efivar = dependency('efivar', version : '>= 33') + conf.set_quoted('EFIVAR_LIBRARY_VERSION', efivar.version()) + efiboot = dependency('efiboot') + objcopy = find_program ('objcopy') + readelf = find_program ('readelf') + + efi_app_location = join_paths(libexecdir, 'fwupd', 'efi') + conf.set_quoted ('EFI_APP_LOCATION', efi_app_location) + + efi_arch = host_machine.cpu_family() + if efi_arch == 'x86' + EFI_MACHINE_TYPE_NAME = 'ia32' + gnu_efi_arch = 'ia32' + elif efi_arch == 'x86_64' + EFI_MACHINE_TYPE_NAME = 'x64' + gnu_efi_arch = 'x86_64' + elif efi_arch == 'arm' + EFI_MACHINE_TYPE_NAME = 'arm' + gnu_efi_arch = 'arm' + elif efi_arch == 'aarch64' + EFI_MACHINE_TYPE_NAME = 'aa64' + gnu_efi_arch = 'aarch64' + else + EFI_MACHINE_TYPE_NAME = '' + gnu_efi_arch = '' endif - if fwup.version().version_compare('>= 12') - conf.set('HAVE_FWUP_VERSION', '1') + conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME) + r = run_command([python3, 'po/test-deps']) + if r.returncode() != 0 + error(r.stderr()) endif - efivar = dependency('efivar') - conf.set_quoted('EFIVAR_LIBRARY_VERSION', efivar.version()) - conf.set_quoted('LIBFWUP_LIBRARY_VERSION', fwup.version()) endif if get_option('plugin_dell') - libsmbios_c = dependency('libsmbios_c', version : '>= 2.3.0') + libsmbios_c = dependency('libsmbios_c', version : '>= 2.4.0') efivar = dependency('efivar') - fwup = dependency('fwup', version : '>= 5') conf.set('HAVE_DELL', '1') + if not get_option('plugin_uefi') + error('plugin_dell also needs plugin_uefi to work') + endif +endif + +if get_option('plugin_modem_manager') + libmm_glib = dependency('mm-glib', version : '>= 1.10.0') + add_project_arguments('-DMM_REQUIRED_VERSION="1.10.0"', language : 'c') + libqmi_glib = dependency('qmi-glib', version : '>= 1.22.0') + add_project_arguments('-DQMI_REQUIRED_VERSION="1.23.1"', language : 'c') +endif + +if get_option('plugin_nvme') + if not cc.has_header('linux/nvme_ioctl.h') + error('NVMe support requires kernel >= 4.4') + endif endif if get_option('plugin_synaptics') @@ -224,26 +278,25 @@ conf.set('HAVE_THUNDERBOLT', '1') endif +if get_option('plugin_flashrom') + libflashrom = dependency('flashrom', fallback : ['flashrom', 'flashrom_dep']) +endif + if get_option('systemd') - systemd = dependency('systemd', version : '>= 231') + systemd = dependency('systemd', version : '>= 211') conf.set('HAVE_SYSTEMD' , '1') + conf.set('HAVE_LOGIND' , '1') +endif + +if get_option('elogind') + elogind = dependency('libelogind', version : '>= 211') + conf.set('HAVE_LOGIND' , '1') endif if get_option('consolekit') conf.set('HAVE_CONSOLEKIT' , '1') endif -prefix = get_option('prefix') - -bindir = join_paths(prefix, get_option('bindir')) -libdir = join_paths(prefix, get_option('libdir')) -datadir = join_paths(prefix, get_option('datadir')) -libexecdir = join_paths(prefix, get_option('libexecdir')) -sysconfdir = join_paths(prefix, get_option('sysconfdir')) -localstatedir = join_paths(prefix, get_option('localstatedir')) -mandir = join_paths(prefix, get_option('mandir')) -localedir = join_paths(prefix, get_option('localedir')) - systemdunitdir = get_option('systemdunitdir') if systemdunitdir == '' and get_option('systemd') systemdunitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') @@ -270,23 +323,21 @@ 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 plugin_deps += gusb plugin_deps += soup plugin_deps += libarchive +plugin_deps += gudev subdir('data') -subdir('docs') +if get_option('gtkdoc') + gtkdocscan = find_program('gtkdoc-scan', required : true) + subdir('docs') +endif subdir('libfwupd') subdir('po') if get_option('daemon') diff -Nru fwupd-1.0.9/meson_options.txt fwupd-1.2.10/meson_options.txt --- fwupd-1.0.9/meson_options.txt 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/meson_options.txt 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,7 @@ -option('bootdir', type : 'string', value : '/boot/efi', description : 'Directory for EFI system partition') option('daemon', type : 'boolean', value : true, description : 'enable the fwupd daemon') +option('agent', type : 'boolean', value : true, description : 'enable the fwupd agent') option('consolekit', type : 'boolean', value : true, description : 'enable ConsoleKit support') +option('firmware-packager', type : 'boolean', value : true, description : 'enable firmware-packager installation') option('gpg', type : 'boolean', value : true, description : 'enable the GPG verification support') option('gtkdoc', type : 'boolean', value : true, description : 'enable developer documentation') option('introspection', type : 'boolean', value : true, description : 'generate GObject Introspection data') @@ -13,9 +14,19 @@ option('plugin_dummy', type : 'boolean', value : false, description : 'enable the dummy device') option('plugin_synaptics', type: 'boolean', value: true, description : 'enable Synaptics MST hub support') option('plugin_thunderbolt', type : 'boolean', value : true, description : 'enable Thunderbolt support') -option('plugin_uefi_labels', type : 'boolean', value : true, description : 'enable UEFI labels support') +option('plugin_redfish', type : 'boolean', value : true, description : 'enable Redfish support') option('plugin_uefi', type : 'boolean', value : true, description : 'enable UEFI support') +option('plugin_nvme', type : 'boolean', value : true, description : 'enable NVMe support') +option('plugin_modem_manager', type : 'boolean', value : false, description : 'enable ModemManager support') +option('plugin_flashrom', type : 'boolean', value : false, description : 'enable libflashrom support') option('systemd', type : 'boolean', value : true, description : 'enable systemd support') option('systemdunitdir', type: 'string', value: '', description: 'Directory for systemd units') +option('elogind', type : 'boolean', value : false, description : 'enable elogind support') option('tests', type : 'boolean', value : true, description : 'enable tests') option('udevdir', type: 'string', value: '', description: 'Directory for udev rules') +option('efi-cc', type : 'string', value : 'gcc', description : 'the compiler to use for EFI modules') +option('efi-ld', type : 'string', value : 'ld', description : 'the linker to use for EFI modules') +option('efi-libdir', type : 'string', description : 'path to the EFI lib directory') +option('efi-ldsdir', type : 'string', description : 'path to the EFI lds directory') +option('efi-includedir', type : 'string', value : '/usr/include/efi', description : 'path to the EFI header directory') +option('efi_os_dir', type: 'string', description : 'the name of OS directory in ESP') diff -Nru fwupd-1.0.9/NEWS fwupd-1.2.10/NEWS --- fwupd-1.0.9/NEWS 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/NEWS 1970-01-01 00:00:00.000000000 +0000 @@ -1,794 +0,0 @@ -Version 1.0.9 -~~~~~~~~~~~~~ -Released: 2018-09-11 - -New Features: - - Add support for more Wacom tablets (Richard Hughes) - - Add support for the Synaptics Panamera chip (Mario Limonciello) - - Add validation for Titan Ridge Thunderbolt devices (Andrei Emeltchenko) - - Use boltd force power API if available (Mario Limonciello) - -Bugfixes: - - Allow flashing Unifying devices in recovery mode (Richard Hughes) - - Allow updating just one specific device from the command line (Richard Hughes) - - Do not hold hidraw devices open forever (Richard Hughes) - - Do not use 8bitdo bootloader commands after a successful flash (Richard Hughes) - - Don't crash when the Dell alternate device has not been set (Richard Hughes) - - Don't potentially expose user passwords for remotes (Richard Hughes) - - Fix a potential buffer overflow when applying a DFU patch (Richard Hughes) - - Fix a potential segfault in smbios data processing (Mario Limonciello) - - Fix downgrading older releases to devices (Richard Hughes) - - Fix failure to detach Unifying devices when using a slow host controller (Richard Hughes) - - Fix flashing devices that require a manual replug (Richard Hughes) - - Handle -EAGAIN from the kernel better when reading NVM (Mario Limonciello) - -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 -Nru fwupd-1.0.9/plugins/altos/altos.quirk fwupd-1.2.10/plugins/altos/altos.quirk --- fwupd-1.0.9/plugins/altos/altos.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/altos.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,7 @@ +# ChaosKey +[DeviceInstanceId=USB\VID_1D50&PID_60C6] +Plugin = altos +Flags = none +[DeviceInstanceId=USB\VID_FFFE&PID_000A] +Plugin = altos +Flags = is-bootloader diff -Nru fwupd-1.0.9/plugins/altos/fu-altos-device.c fwupd-1.2.10/plugins/altos/fu-altos-device.c --- fwupd-1.0.9/plugins/altos/fu-altos-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/fu-altos-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,14 +6,13 @@ #include "config.h" -#include #include -#include #include #include #include #include +#include "fu-io-channel.h" #include "fu-altos-device.h" #include "fu-altos-firmware.h" @@ -28,7 +26,7 @@ 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) @@ -152,60 +150,15 @@ 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 * @@ -214,90 +167,12 @@ 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 @@ -307,18 +182,12 @@ 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, @@ -343,7 +212,8 @@ 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, @@ -362,10 +232,11 @@ 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; } @@ -398,7 +269,10 @@ } static gboolean -fu_altos_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +fu_altos_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) { FuAltosDevice *self = FU_ALTOS_DEVICE (device); GBytes *fw_blob; @@ -641,7 +515,8 @@ /* version number */ if (g_str_has_prefix (lines[i], "software-version ")) { - fu_device_set_version (FU_DEVICE (self), lines[i] + 17); + fu_device_set_version (FU_DEVICE (self), lines[i] + 17, + FWUPD_VERSION_FORMAT_TRIPLET); continue; } @@ -663,9 +538,10 @@ return TRUE; } -gboolean -fu_altos_device_probe (FuAltosDevice *self, GError **error) +static gboolean +fu_altos_device_probe (FuDevice *device, GError **error) { + FuAltosDevice *self = FU_ALTOS_DEVICE (device); GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); /* bootloader uses tty */ @@ -699,7 +575,8 @@ version); return FALSE; } - fu_device_set_version (FU_DEVICE (self), version + 19); + fu_device_set_version (FU_DEVICE (self), version + 19, + FWUPD_VERSION_FORMAT_TRIPLET); } /* success */ @@ -750,6 +627,7 @@ { GObjectClass *object_class = G_OBJECT_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->probe = fu_altos_device_probe; klass_device->write_firmware = fu_altos_device_write_firmware; klass_device->read_firmware = fu_altos_device_read_firmware; object_class->finalize = fu_altos_device_finalize; @@ -762,7 +640,7 @@ } FuAltosDeviceVidPid; FuAltosDevice * -fu_altos_device_new (GUsbDevice *usb_device) +fu_altos_device_new (FuUsbDevice *device) { const FuAltosDeviceVidPid vidpids[] = { { 0xfffe, 0x000a, FU_ALTOS_DEVICE_KIND_BOOTLOADER }, @@ -772,12 +650,10 @@ /* set kind */ for (guint j = 0; vidpids[j].vid != 0x0000; j++) { - if (g_usb_device_get_vid (usb_device) == vidpids[j].vid && - g_usb_device_get_pid (usb_device) == vidpids[j].pid) { - FuAltosDevice *self; - self = g_object_new (FU_TYPE_ALTOS_DEVICE, - "usb-device", usb_device, - NULL); + if (fu_usb_device_get_vid (device) == vidpids[j].vid && + fu_usb_device_get_pid (device) == vidpids[j].pid) { + FuAltosDevice *self = g_object_new (FU_TYPE_ALTOS_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); self->kind = vidpids[j].kind; fu_altos_device_init_real (self); return self; diff -Nru fwupd-1.0.9/plugins/altos/fu-altos-device.h fwupd-1.2.10/plugins/altos/fu-altos-device.h --- fwupd-1.0.9/plugins/altos/fu-altos-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/fu-altos-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_ALTOS_DEVICE_H -#define __FU_ALTOS_DEVICE_H - -#include -#include +#pragma once #include "fu-plugin.h" @@ -33,13 +28,9 @@ FU_ALTOS_DEVICE_WRITE_FIRMWARE_FLAG_LAST } FuAltosDeviceWriteFirmwareFlag; -FuAltosDevice *fu_altos_device_new (GUsbDevice *usb_device); +FuAltosDevice *fu_altos_device_new (FuUsbDevice *device); FuAltosDeviceKind fu_altos_device_kind_from_string (const gchar *kind); const gchar *fu_altos_device_kind_to_string (FuAltosDeviceKind kind); FuAltosDeviceKind fu_altos_device_get_kind (FuAltosDevice *device); -gboolean fu_altos_device_probe (FuAltosDevice *device, - GError **error); G_END_DECLS - -#endif /* __FU_ALTOS_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/altos/fu-altos-firmware.c fwupd-1.2.10/plugins/altos/fu-altos-firmware.c --- fwupd-1.0.9/plugins/altos/fu-altos-firmware.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/fu-altos-firmware.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/altos/fu-altos-firmware.h fwupd-1.2.10/plugins/altos/fu-altos-firmware.h --- fwupd-1.0.9/plugins/altos/fu-altos-firmware.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/fu-altos-firmware.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_ALTOS_FIRMWARE_H -#define __FU_ALTOS_FIRMWARE_H - -#include +#pragma once G_BEGIN_DECLS @@ -24,5 +20,3 @@ GError **error); G_END_DECLS - -#endif /* __FU_ALTOS_FIRMWARE_H */ diff -Nru fwupd-1.0.9/plugins/altos/fu-plugin-altos.c fwupd-1.2.10/plugins/altos/fu-plugin-altos.c --- fwupd-1.0.9/plugins/altos/fu-plugin-altos.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/fu-plugin-altos.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,25 +6,33 @@ #include "config.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" #include "fu-altos-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.altusmetrum.altos"); +} + gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) { + GUsbDevice *usb_device = fu_usb_device_get_dev (device); const gchar *platform_id = NULL; g_autofree gchar *runtime_id = NULL; g_autoptr(FuAltosDevice) dev = NULL; /* get kind */ - dev = fu_altos_device_new (usb_device); + dev = fu_altos_device_new (device); if (dev == NULL) return TRUE; /* get device properties */ - if (!fu_altos_device_probe (dev, error)) + if (!fu_device_probe (FU_DEVICE (dev), error)) return FALSE; /* only the bootloader can do the update */ @@ -37,9 +44,10 @@ if (dev_runtime != NULL) { const gchar *guid = fu_device_get_guid_default (dev_runtime); g_debug ("adding runtime GUID of %s", guid); - fu_device_add_guid (FU_DEVICE (dev), guid); + fu_device_add_counterpart_guid (FU_DEVICE (dev), guid); fu_device_set_version (FU_DEVICE (dev), - fu_device_get_version (dev_runtime)); + fu_device_get_version (dev_runtime), + fu_device_get_version_format (dev_runtime)); } } else { fu_plugin_cache_add (plugin, runtime_id, dev); @@ -83,5 +91,5 @@ GError **error) { fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); - return fu_device_write_firmware (dev, blob_fw, error); + return fu_device_write_firmware (dev, blob_fw, flags, error); } diff -Nru fwupd-1.0.9/plugins/altos/meson.build fwupd-1.2.10/plugins/altos/meson.build --- fwupd-1.0.9/plugins/altos/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,11 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginAltos"'] +install_data(['altos.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_altos', + fu_hash, sources : [ 'fu-altos-device.c', 'fu-altos-firmware.c', @@ -13,9 +18,11 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ - gudev, libelf, plugin_deps, ], diff -Nru fwupd-1.0.9/plugins/altos/README.md fwupd-1.2.10/plugins/altos/README.md --- fwupd-1.0.9/plugins/altos/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/altos/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -15,6 +15,25 @@ 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 -Nru fwupd-1.0.9/plugins/amt/fu-plugin-amt.c fwupd-1.2.10/plugins/amt/fu-plugin-amt.c --- fwupd-1.0.9/plugins/amt/fu-plugin-amt.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/amt/fu-plugin-amt.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2012 Intel Corporation. All rights reserved. +/* + * Copyright (C) 2012 Intel Corporation. * Copyright (C) 2017 Google, Inc. * Copyright (C) 2017 Richard Hughes * @@ -14,9 +13,7 @@ #include #include #include -#include -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" typedef struct { @@ -88,27 +85,26 @@ 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) { @@ -120,7 +116,6 @@ 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, @@ -128,16 +123,22 @@ 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) { @@ -145,7 +146,7 @@ FWUPD_ERROR, FWUPD_ERROR_WRITE, "write failed on timeout with status"); - return 0; + return FALSE; } /* rc < 0 */ @@ -153,7 +154,7 @@ FWUPD_ERROR, FWUPD_ERROR_WRITE, "write failed on select with status %zd", rc); - return rc; + return FALSE; } /*************************************************************************** @@ -256,50 +257,91 @@ 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; + } + 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; } - return AMT_STATUS_SUCCESS; + 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, @@ -311,56 +353,86 @@ { 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) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Unable to get provisioning state"); + 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; } @@ -373,13 +445,14 @@ 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; + fwupd_guid_t uu; + g_autofree gchar *guid_buf = NULL; 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, \ @@ -390,34 +463,24 @@ 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) - 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"); + 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; } - 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)); dev = fu_device_new (); - fu_device_set_id (dev, "/dev/mei"); + fu_device_set_id (dev, "/dev/mei0"); fu_device_set_vendor (dev, "Intel Corporation"); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_icon (dev, "computer"); @@ -443,24 +506,44 @@ /* add guid */ memcpy (&uu, &ctx->guid, 16); - uuid_unparse (uu, guid_buf); + guid_buf = fwupd_guid_to_string ((const fwupd_guid_t *) &uu, FWUPD_GUID_FLAG_NONE); fu_device_add_guid (dev, guid_buf); /* 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) { + 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 Version") == 0) { - fu_device_set_version_bootloader (dev, ver.versions[i].version.string); + 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, FWUPD_VERSION_FORMAT_INTEL_ME); + 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 -Nru fwupd-1.0.9/plugins/amt/meson.build fwupd-1.2.10/plugins/amt/meson.build --- fwupd-1.0.9/plugins/amt/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/amt/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginAmt"'] shared_module('fu_plugin_amt', + fu_hash, sources : [ 'fu-plugin-amt.c', ], @@ -11,9 +12,11 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, - uuid, ], ) diff -Nru fwupd-1.0.9/plugins/amt/README.md fwupd-1.2.10/plugins/amt/README.md --- fwupd-1.0.9/plugins/amt/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/amt/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -15,3 +15,8 @@ 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 -Nru fwupd-1.0.9/plugins/ata/ata.quirk fwupd-1.2.10/plugins/ata/ata.quirk --- fwupd-1.0.9/plugins/ata/ata.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ata/ata.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru fwupd-1.0.9/plugins/ata/fu-ata-device.c fwupd-1.2.10/plugins/ata/fu-ata-device.c --- fwupd-1.0.9/plugins/ata/fu-ata-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ata/fu-ata-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,728 @@ +/* + * 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_FLUSH_CACHE 0xe7 +#define ATA_OP_DOWNLOAD_MICROCODE 0x92 +#define ATA_OP_STANDBY_IMMEDIATE 0xe0 + +#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; + guint usb_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_strstrip (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); + g_string_append_printf (str, " usb-depth:\t\t%u\n", self->usb_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 gchar * +fu_ata_device_get_guid_safe (const guint16 *buf, guint16 addr_start) +{ + if (!fu_common_guid_is_plausible ((guint8 *) (buf + addr_start))) + return NULL; + return fwupd_guid_to_string ((const fwupd_guid_t *) (buf + addr_start), + FWUPD_GUID_FLAG_MIXED_ENDIAN); +} + +static void +fu_ata_device_parse_id_maybe_dell (FuAtaDevice *self, const guint16 *buf) +{ + g_autofree gchar *component_id = NULL; + g_autofree gchar *guid_efi = NULL; + g_autofree gchar *guid_id = NULL; + g_autofree gchar *guid = NULL; + + /* add extra component ID if set */ + component_id = fu_ata_device_get_string (buf, 137, 140); + if (component_id == NULL || + !g_str_is_ascii (component_id) || + strlen (component_id) < 6) { + g_debug ("invalid component ID, skipping"); + return; + } + + /* do not add the FuUdevDevice instance IDs as generic firmware + * should not be used on these OEM-specific devices */ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS); + + /* add instance ID *and* GUID as using no-auto-instance-ids */ + guid_id = g_strdup_printf ("STORAGE-DELL-%s", component_id); + fu_device_add_instance_id (FU_DEVICE (self), guid_id); + guid = fwupd_guid_hash_string (guid_id); + fu_device_add_guid (FU_DEVICE (self), guid); + + /* also add the EFI GUID */ + guid_efi = fu_ata_device_get_guid_safe (buf, 129); + if (guid_efi != NULL) + fu_device_add_guid (FU_DEVICE (self), guid_efi); +} + +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; + } + + fu_ata_device_parse_id_maybe_dell (self, id); + + /* 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); + if (tmp != NULL) + 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); + if (tmp != NULL) + 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); + if (tmp != NULL) + fu_device_set_version (device, tmp, FWUPD_VERSION_FORMAT_PLAIN); + } else { + fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_PLAIN); + } + + /* 8 byte additional product identifier == SKU? */ + sku = fu_ata_device_get_string (id, 170, 173); + if (sku != NULL) + g_debug ("SKU=%s", sku); + + /* if we have vendor defined identify blocks don't add generic GUID */ + if (fu_device_get_guids (device)->len != 0) + return TRUE; + + /* add extra GUIDs if none detected from identify block */ + 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_instance_id (device, tmp); + } + if (name_pad != NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("IDE\\0%s", name_pad); + fu_device_add_instance_id (device, tmp); + } + + /* add the name fallback */ + fu_device_add_instance_id (device, fu_device_get_name (device)); + + 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 and USB depth to work out if in an external enclosure */ + self->pci_depth = fu_udev_device_get_slot_depth (device, "pci"); + self->usb_depth = fu_udev_device_get_slot_depth (device, "usb"); + if (self->pci_depth <= 2 && self->usb_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; + + /* success */ + return TRUE; +} + +static gboolean +fu_ata_device_activate (FuDevice *device, GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + struct ata_tf tf = { 0x0 }; + + + /* flush cache and put drive in standby to prepare to activate */ + tf.dev = ATA_USING_LBA; + tf.command = ATA_OP_FLUSH_CACHE; + if (!fu_ata_device_command (self, &tf, SG_DXFER_NONE, + 120 * 1000, /* a long time! */ + NULL, 0, error)) { + g_prefix_error (error, "failed to flush cache immediate: "); + return FALSE; + } + tf.command = ATA_OP_STANDBY_IMMEDIATE; + if (!fu_ata_device_command (self, &tf, SG_DXFER_NONE, + 120 * 1000, /* a long time! */ + NULL, 0, error)) { + g_prefix_error (error, "failed to standby immediate: "); + return FALSE; + } + + /* load the new firmware */ + tf.dev = 0xa0 | ATA_USING_LBA; + tf.command = ATA_OP_DOWNLOAD_MICROCODE; + tf.feat = ATA_SUBCMD_MICROCODE_ACTIVATE; + if (!fu_ata_device_command (self, &tf, SG_DXFER_NONE, + 120 * 1000, /* a long time! */ + NULL, 0, error)) { + g_prefix_error (error, "failed to activate firmware: "); + return FALSE; + } + + /* 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, + FwupdInstallFlags flags, + GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE (device); + guint32 chunksz = (guint32) self->transfer_blocks * FU_ATA_BLOCK_SIZE; + guint max_size = 0xffff * FU_ATA_BLOCK_SIZE; + g_autoptr(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_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + 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->activate = fu_ata_device_activate; + 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 -Nru fwupd-1.0.9/plugins/ata/fu-ata-device.h fwupd-1.2.10/plugins/ata/fu-ata-device.h --- fwupd-1.0.9/plugins/ata/fu-ata-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ata/fu-ata-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/ata/fu-plugin-ata.c fwupd-1.2.10/plugins/ata/fu-plugin-ata.c --- fwupd-1.0.9/plugins/ata/fu-plugin-ata.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ata/fu-plugin-ata.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,70 @@ +/* + * 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, flags, error); +} + +gboolean +fu_plugin_activate (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_activate (device, error); +} diff -Nru fwupd-1.0.9/plugins/ata/fu-self-test.c fwupd-1.2.10/plugins/ata/fu-self-test.c --- fwupd-1.0.9/plugins/ata/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ata/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/ata/meson.build fwupd-1.2.10/plugins/ata/meson.build --- fwupd-1.0.9/plugins/ata/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ata/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,59 @@ +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', + fu_hash, + 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 -Nru fwupd-1.0.9/plugins/ata/README.md fwupd-1.2.10/plugins/ata/README.md --- fwupd-1.0.9/plugins/ata/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ata/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 | Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/ata/tests/StarDrive-SBFM61.2.bin and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/ata/tests/StarDrive-SBFM61.2.bin differ diff -Nru fwupd-1.0.9/plugins/colorhug/colorhug.quirk fwupd-1.2.10/plugins/colorhug/colorhug.quirk --- fwupd-1.0.9/plugins/colorhug/colorhug.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/colorhug.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -1,41 +1,55 @@ -### ColorHug1 ################################################################## -[FuColorhugDevice] -USB\VID_273F&PID_1000=bootloader -USB\VID_273F&PID_1001=none - -[FuUsbDevice:summary] -USB\VID_273F&PID_1001=An open source display colorimeter - -[FuUsbDevice:icon] -USB\VID_273F&PID_1001=colorimeter-colorhug - -[FuUsbDevice:guid] -USB\VID_273F&PID_1000=40338ceb-b966-4eae-adae-9c32edfcc484 -USB\VID_273F&PID_1001=40338ceb-b966-4eae-adae-9c32edfcc484 - -### ColorHug2 ################################################################## -[FuColorhugDevice] -USB\VID_273F&PID_1004=none -USB\VID_273F&PID_1005=bootloader - -[FuUsbDevice:summary] -USB\VID_273F&PID_1004=An open source display colorimeter - -[FuUsbDevice:icon] -USB\VID_273F&PID_1004=colorimeter-colorhug - -[FuUsbDevice:guid] -USB\VID_273F&PID_1004=2082b5e0-7a64-478a-b1b2-e3404fab6dad -USB\VID_273F&PID_1005=2082b5e0-7a64-478a-b1b2-e3404fab6dad - -### ColorHugALS ################################################################ -[FuColorhugDevice] -USB\VID_273F&PID_1006=halfsize,bootloader -USB\VID_273F&PID_1007=halfsize,none - -[FuUsbDevice:summary] -USB\VID_273F&PID_1007=An open source ambient light sensor - -[FuUsbDevice:guid] -USB\VID_273F&PID_1006=84f40464-9272-4ef7-9399-cd95f12da696 -USB\VID_273F&PID_1007=84f40464-9272-4ef7-9399-cd95f12da696 +# ColorHug1 +[DeviceInstanceId=USB\VID_273F&PID_1000] +Plugin = colorhug +Flags = is-bootloader +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 +Flags = none +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] +Plugin = colorhug +Flags = none +Summary = An open source display colorimeter +Icon = colorimeter-colorhug +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] +Plugin = colorhug +Flags = halfsize,none +Summary = An open source ambient light sensor +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 -Nru fwupd-1.0.9/plugins/colorhug/fu-colorhug-common.c fwupd-1.2.10/plugins/colorhug/fu-colorhug-common.c --- fwupd-1.0.9/plugins/colorhug/fu-colorhug-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/fu-colorhug-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#include "config.h" + #include "fu-colorhug-common.h" const gchar * @@ -84,17 +85,3 @@ 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 -Nru fwupd-1.0.9/plugins/colorhug/fu-colorhug-common.h fwupd-1.2.10/plugins/colorhug/fu-colorhug-common.h --- fwupd-1.0.9/plugins/colorhug/fu-colorhug-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/fu-colorhug-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,12 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_COLORHUG_COMMON_H -#define __FU_COLORHUG_COMMON_H +#pragma once #include -#include G_BEGIN_DECLS @@ -54,10 +51,5 @@ } ChError; const gchar *ch_strerror (ChError error_enum); -void ch_buffer_dump (const gchar *title, - const guint8 *buf, - gsize sz); G_END_DECLS - -#endif /* __FU_COLORHUG_COMMON_H */ diff -Nru fwupd-1.0.9/plugins/colorhug/fu-colorhug-device.c fwupd-1.2.10/plugins/colorhug/fu-colorhug-device.c --- fwupd-1.0.9/plugins/colorhug/fu-colorhug-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/fu-colorhug-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -8,13 +7,21 @@ #include "config.h" #include -#include - -#include "dfu-chunked.h" +#include "fu-chunk.h" #include "fu-colorhug-common.h" #include "fu-colorhug-device.h" +/** + * FU_COLORHUG_DEVICE_FLAG_HALFSIZE: + * + * Some devices have a compact memory layout and the application code starts + * earlier. + * + * Since: 1.0.3 + */ +#define FU_COLORHUG_DEVICE_FLAG_HALFSIZE "halfsize" + struct _FuColorhugDevice { FuUsbDevice parent_instance; guint16 start_addr; @@ -75,7 +82,8 @@ 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, @@ -108,7 +116,8 @@ 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 && @@ -117,7 +126,7 @@ g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "request not all recieved, got %" G_GSIZE_FORMAT, + "request not all received, got %" G_GSIZE_FORMAT, actual_length); return FALSE; } @@ -255,31 +264,11 @@ fu_colorhug_device_probe (FuUsbDevice *device, GError **error) { FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); - const gchar *quirk_str; - g_auto(GStrv) quirks = NULL; - /* devices have to be whitelisted */ - quirk_str = fu_device_get_plugin_hints (FU_DEVICE (device)); - if (quirk_str == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported with this device"); - return FALSE; - } - fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - quirks = g_strsplit (quirk_str, ",", -1); - for (guint i = 0; quirks[i] != NULL; i++) { - if (g_strcmp0 (quirks[i], "bootloader") == 0) { - fu_device_add_flag (FU_DEVICE (self), - FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - continue; - } - if (g_strcmp0 (quirks[i], "halfsize") == 0) { - self->start_addr = CH_EEPROM_ADDR_RUNCODE_ALS; - continue; - } - } + /* compact memory layout */ + if (fu_device_has_custom_flag (FU_DEVICE (device), + FU_COLORHUG_DEVICE_FLAG_HALFSIZE)) + self->start_addr = CH_EEPROM_ADDR_RUNCODE_ALS; /* add hardcoded bits */ fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); @@ -291,7 +280,6 @@ static gboolean fu_colorhug_device_open (FuUsbDevice *device, GError **error) { - FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); GUsbDevice *usb_device = fu_usb_device_get_dev (device); /* got the version using the HID API */ @@ -302,13 +290,23 @@ error)) { return FALSE; } + + /* success */ + return TRUE; +} + +static gboolean +fu_colorhug_device_setup (FuDevice *device, GError **error) +{ + FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); + if (fu_device_get_version (FU_DEVICE (device)) == NULL) { g_autofree gchar *version = NULL; g_autoptr(GError) error_local = NULL; version = fu_colorhug_device_get_version (self, &error_local); if (version != NULL) { g_debug ("obtained fwver using API '%s'", version); - fu_device_set_version (FU_DEVICE (device), version); + fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_TRIPLET); } else { g_warning ("failed to get firmware version: %s", error_local->message); @@ -329,16 +327,19 @@ } static gboolean -fu_colorhug_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +fu_colorhug_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) { FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); g_autoptr(GPtrArray) chunks = NULL; /* build packets */ - chunks = dfu_chunked_new_from_bytes (fw, - self->start_addr, - 0x00, /* page_sz */ - CH_FLASH_TRANSFER_BLOCK_SIZE); + chunks = fu_chunk_array_new_from_bytes (fw, + self->start_addr, + 0x00, /* page_sz */ + CH_FLASH_TRANSFER_BLOCK_SIZE); /* don't auto-boot firmware */ fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); @@ -351,15 +352,15 @@ /* write each block */ for (guint i = 0; i < chunks->len; i++) { - DfuChunkedPacket *pkt = g_ptr_array_index (chunks, i); + FuChunk *chk = g_ptr_array_index (chunks, i); guint8 buf[CH_FLASH_TRANSFER_BLOCK_SIZE+4]; g_autoptr(GError) error_local = NULL; /* set address, length, checksum, data */ - fu_common_write_uint16 (buf + 0, pkt->address, G_LITTLE_ENDIAN); - buf[2] = pkt->data_sz; - buf[3] = ch_colorhug_device_calculate_checksum (pkt->data, pkt->data_sz); - memcpy (buf + 4, pkt->data, pkt->data_sz); + fu_common_write_uint16 (buf + 0, chk->address, G_LITTLE_ENDIAN); + buf[2] = chk->data_sz; + buf[3] = ch_colorhug_device_calculate_checksum (chk->data, chk->data_sz); + memcpy (buf + 4, chk->data, chk->data_sz); if (!fu_colorhug_device_msg (self, CH_CMD_WRITE_FLASH, buf, sizeof(buf), /* in */ NULL, 0, /* out */ @@ -379,14 +380,14 @@ /* verify each block */ fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY); for (guint i = 0; i < chunks->len; i++) { - DfuChunkedPacket *pkt = g_ptr_array_index (chunks, i); + FuChunk *chk = g_ptr_array_index (chunks, i); guint8 buf[3]; guint8 buf_out[CH_FLASH_TRANSFER_BLOCK_SIZE+1]; g_autoptr(GError) error_local = NULL; /* set address */ - fu_common_write_uint16 (buf + 0, pkt->address, G_LITTLE_ENDIAN); - buf[2] = pkt->data_sz; + fu_common_write_uint16 (buf + 0, chk->address, G_LITTLE_ENDIAN); + buf[2] = chk->data_sz; if (!fu_colorhug_device_msg (self, CH_CMD_READ_FLASH, buf, sizeof(buf), /* in */ buf_out, sizeof(buf_out), /* out */ @@ -400,13 +401,13 @@ } /* verify */ - if (memcmp (buf_out + 1, pkt->data, pkt->data_sz) != 0) { + if (memcmp (buf_out + 1, chk->data, chk->data_sz) != 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "failed to verify firmware for chunk %u, " "address 0x%0x, length 0x%0x", - i, (guint) pkt->address, pkt->data_sz); + i, (guint) chk->address, chk->data_sz); return FALSE; } @@ -425,6 +426,8 @@ { /* this is the application code */ self->start_addr = CH_EEPROM_ADDR_RUNCODE; + fu_device_set_remove_delay (FU_DEVICE (self), + FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); } static void @@ -435,16 +438,16 @@ klass_device->write_firmware = fu_colorhug_device_write_firmware; klass_device->attach = fu_colorhug_device_attach; klass_device->detach = fu_colorhug_device_detach; + klass_device->setup = fu_colorhug_device_setup; klass_usb_device->open = fu_colorhug_device_open; klass_usb_device->probe = fu_colorhug_device_probe; } FuColorhugDevice * -fu_colorhug_device_new (GUsbDevice *usb_device) +fu_colorhug_device_new (FuUsbDevice *device) { FuColorhugDevice *self = NULL; - self = g_object_new (FU_TYPE_COLORHUG_DEVICE, - "usb-device", usb_device, - NULL); + self = g_object_new (FU_TYPE_COLORHUG_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); return self; } diff -Nru fwupd-1.0.9/plugins/colorhug/fu-colorhug-device.h fwupd-1.2.10/plugins/colorhug/fu-colorhug-device.h --- fwupd-1.0.9/plugins/colorhug/fu-colorhug-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/fu-colorhug-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_COLORHUG_DEVICE_H -#define __FU_COLORHUG_DEVICE_H - -#include -#include +#pragma once #include "fu-plugin.h" @@ -18,7 +13,7 @@ #define FU_TYPE_COLORHUG_DEVICE (fu_colorhug_device_get_type ()) G_DECLARE_FINAL_TYPE (FuColorhugDevice, fu_colorhug_device, FU, COLORHUG_DEVICE, FuUsbDevice) -FuColorhugDevice *fu_colorhug_device_new (GUsbDevice *usb_device); +FuColorhugDevice *fu_colorhug_device_new (FuUsbDevice *device); /* object methods */ gboolean fu_colorhug_device_set_flash_success (FuColorhugDevice *device, @@ -26,5 +21,3 @@ GError **error); G_END_DECLS - -#endif /* __FU_COLORHUG_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/colorhug/fu-plugin-colorhug.c fwupd-1.2.10/plugins/colorhug/fu-plugin-colorhug.c --- fwupd-1.0.9/plugins/colorhug/fu-plugin-colorhug.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/fu-plugin-colorhug.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,18 +6,22 @@ #include "config.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" #include "fu-colorhug-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.hughski.colorhug"); +} + gboolean fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) { - FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GUsbDevice) usb_device2 = NULL; /* open device */ locker = fu_device_locker_new (device, error); @@ -36,29 +39,14 @@ return FALSE; /* wait for replug */ - g_clear_object (&locker); - usb_device2 = g_usb_context_wait_for_replug (fu_plugin_get_usb_context (plugin), - usb_device, - 10000, error); - if (usb_device2 == NULL) { - g_prefix_error (error, "device did not come back: "); - return FALSE; - } - - /* set the new device until we can use a new FuDevice */ - fu_usb_device_set_dev (FU_USB_DEVICE (self), usb_device2); - - /* success */ + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } gboolean fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) { - FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GUsbDevice) usb_device2 = NULL; /* open device */ locker = fu_device_locker_new (device, error); @@ -76,19 +64,7 @@ return FALSE; /* wait for replug */ - g_clear_object (&locker); - usb_device2 = g_usb_context_wait_for_replug (fu_plugin_get_usb_context (plugin), - usb_device, - 10000, error); - if (usb_device2 == NULL) { - g_prefix_error (error, "device did not come back: "); - return FALSE; - } - - /* set the new device until we can use a new FuDevice */ - fu_usb_device_set_dev (FU_USB_DEVICE (self), usb_device2); - - /* success */ + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } @@ -120,23 +96,22 @@ locker = fu_device_locker_new (device, error); if (locker == NULL) return FALSE; - return fu_device_write_firmware (device, blob_fw, error); + return fu_device_write_firmware (device, blob_fw, flags, error); } gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) { g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuColorhugDevice) device = NULL; + g_autoptr(FuColorhugDevice) dev = NULL; /* open the device */ - device = fu_colorhug_device_new (usb_device); - fu_device_set_quirks (FU_DEVICE (device), fu_plugin_get_quirks (plugin)); - locker = fu_device_locker_new (device, error); + dev = fu_colorhug_device_new (device); + locker = fu_device_locker_new (dev, error); if (locker == NULL) return FALSE; /* insert to hash */ - fu_plugin_device_add (plugin, FU_DEVICE (device)); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } diff -Nru fwupd-1.0.9/plugins/colorhug/meson.build fwupd-1.2.10/plugins/colorhug/meson.build --- fwupd-1.0.9/plugins/colorhug/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -7,6 +7,7 @@ ) shared_module('fu_plugin_colorhug', + fu_hash, sources : [ 'fu-colorhug-common.c', 'fu-colorhug-device.c', @@ -14,17 +15,16 @@ ], include_directories : [ include_directories('../..'), - include_directories('../dfu'), include_directories('../../src'), include_directories('../../libfwupd'), ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, ], - link_with : [ - dfu, - ], ) diff -Nru fwupd-1.0.9/plugins/colorhug/README.md fwupd-1.2.10/plugins/colorhug/README.md --- fwupd-1.0.9/plugins/colorhug/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/colorhug/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -10,3 +10,22 @@ 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 -Nru fwupd-1.0.9/plugins/csr/csr-aiaiai-h05.quirk fwupd-1.2.10/plugins/csr/csr-aiaiai-h05.quirk --- fwupd-1.0.9/plugins/csr/csr-aiaiai-h05.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/csr-aiaiai-h05.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -[FuCsrDevice] - -# AIAIAI H05 bluetooth headphones -USB\VID_0A12&PID_1337=none - -[FuUsbDevice:name] -USB\VID_0A12&PID_1337=H05 - -[FuUsbDevice:summary] -USB\VID_0A12&PID_1337=Bluetooth headphones - -[FuUsbDevice:icon] -USB\VID_0A12&PID_1337=audio-headphones - -[FuUsbDevice:vendor] -USB\VID_0A12&PID_1337=AIAIAI - -[FuUsbDevice:version] -USB\VID_0A12&PID_1337&REV_2520=1.2 diff -Nru fwupd-1.0.9/plugins/csr/csr-aiaiai-h60.quirk fwupd-1.2.10/plugins/csr/csr-aiaiai-h60.quirk --- fwupd-1.0.9/plugins/csr/csr-aiaiai-h60.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/csr-aiaiai-h60.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -[FuCsrDevice] - -# AIAIAI H60 bluetooth headphones -USB\VID_0A12&PID_4004=none - -[FuUsbDevice:name] -USB\VID_0A12&PID_4004=H60 - -[FuUsbDevice:summary] -USB\VID_0A12&PID_4004=Bluetooth headphones - -[FuUsbDevice:icon] -USB\VID_0A12&PID_4004=audio-headphones - -[FuUsbDevice:vendor] -USB\VID_0A12&PID_4004=AIAIAI diff -Nru fwupd-1.0.9/plugins/csr/csr-aiaiai.quirk fwupd-1.2.10/plugins/csr/csr-aiaiai.quirk --- fwupd-1.0.9/plugins/csr/csr-aiaiai.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/csr-aiaiai.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,15 @@ +[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 + +[DeviceInstanceId=USB\VID_0A12&PID_4004] +Plugin = csr +Name = H60 +Summary = Bluetooth Headphones +Icon = audio-headphones +Vendor = AIAIAI diff -Nru fwupd-1.0.9/plugins/csr/fu-csr-device.c fwupd-1.2.10/plugins/csr/fu-csr-device.c --- fwupd-1.0.9/plugins/csr/fu-csr-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/fu-csr-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -9,12 +8,28 @@ #include +#include "fu-chunk.h" #include "fu-csr-device.h" #include "dfu-common.h" -#include "dfu-chunked.h" #include "dfu-firmware.h" +/** + * FU_CSR_DEVICE_QUIRK_FLAG_REQUIRE_DELAY: + * + * Respect the write timeout value when performing actions. This is sometimes + * set to a huge amount of time, and so is not used by default. + * + * Since: 1.0.3 + */ +#define FU_CSR_DEVICE_FLAG_REQUIRE_DELAY "require-delay" + +typedef enum { + FU_CSR_DEVICE_QUIRK_NONE = 0, + FU_CSR_DEVICE_QUIRK_REQUIRE_DELAY = (1 << 0), + FU_CSR_DEVICE_QUIRK_LAST +} FuCsrDeviceQuirks; + struct _FuCsrDevice { FuUsbDevice parent_instance; @@ -52,23 +67,6 @@ 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"); -} - -void -fu_csr_device_set_quirks (FuCsrDevice *self, FuCsrDeviceQuirks quirks) -{ - self->quirks = quirks; -} - static gboolean fu_csr_device_attach (FuDevice *device, GError **error) { @@ -77,7 +75,8 @@ 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, @@ -126,7 +125,8 @@ 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) { @@ -170,7 +170,8 @@ 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, @@ -221,7 +222,8 @@ 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) { @@ -322,7 +324,6 @@ } /* notify UI */ - fu_device_set_status (device, FWUPD_STATUS_IDLE); return dfu_utils_bytes_join_array (chunks); } @@ -354,7 +355,8 @@ 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, @@ -428,25 +430,19 @@ return dfu_element_get_contents (element); } -static gboolean -fu_csr_device_download (FuDevice *device, GBytes *blob, GError **error) +static GBytes * +fu_csr_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) { - FuCsrDevice *self = FU_CSR_DEVICE (device); GBytes *blob_noftr; - const guint8 *data; - gsize sz = 0; - guint16 idx; g_autoptr(DfuFirmware) dfu_firmware = dfu_firmware_new (); - g_autoptr(GBytes) blob_empty = NULL; - g_autoptr(GPtrArray) packets = NULL; - - /* notify UI */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); /* parse the file */ - if (!dfu_firmware_parse_data (dfu_firmware, blob, + if (!dfu_firmware_parse_data (dfu_firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) - return FALSE; + return NULL; if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) { g_autofree gchar *fw_str = NULL; fw_str = dfu_firmware_to_string (dfu_firmware); @@ -457,7 +453,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "expected DFU firmware"); - return FALSE; + return NULL; } /* get the blob from the firmware file */ @@ -467,18 +463,35 @@ FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "firmware contained no data"); - return FALSE; + return NULL; } - /* create packets */ - data = g_bytes_get_data (blob_noftr, &sz); - packets = dfu_chunked_new (data, (guint32) sz, 0x0, 0x0, - FU_CSR_PACKET_DATA_SIZE - FU_CSR_COMMAND_HEADER_SIZE); + /* success */ + return g_bytes_ref (blob_noftr); +} + +static gboolean +fu_csr_device_download (FuDevice *device, + GBytes *blob, + FwupdInstallFlags flags, + GError **error) +{ + FuCsrDevice *self = FU_CSR_DEVICE (device); + guint16 idx; + g_autoptr(GBytes) blob_empty = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* notify UI */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + + /* create chunks */ + chunks = fu_chunk_array_new_from_bytes (blob, 0x0, 0x0, + FU_CSR_PACKET_DATA_SIZE - FU_CSR_COMMAND_HEADER_SIZE); /* send to hardware */ - for (idx = 0; idx < packets->len; idx++) { - DfuChunkedPacket *pkt = g_ptr_array_index (packets, idx); - g_autoptr(GBytes) blob_tmp = g_bytes_new_static (pkt->data, pkt->data_sz); + for (idx = 0; idx < chunks->len; idx++) { + FuChunk *chk = g_ptr_array_index (chunks, idx); + g_autoptr(GBytes) blob_tmp = g_bytes_new_static (chk->data, chk->data_sz); /* send packet */ if (!fu_csr_device_download_chunk (self, idx, blob_tmp, error)) @@ -486,38 +499,23 @@ /* update progress */ fu_device_set_progress_full (device, - (gsize) idx, (gsize) packets->len); + (gsize) idx, (gsize) chunks->len); } /* all done */ blob_empty = g_bytes_new (NULL, 0); - if (!fu_csr_device_download_chunk (self, idx, blob_empty, error)) - return FALSE; - - /* notify UI */ - fu_device_set_status (device, FWUPD_STATUS_IDLE); - - return TRUE; + return fu_csr_device_download_chunk (self, idx, blob_empty, error); } static gboolean fu_csr_device_probe (FuUsbDevice *device, GError **error) { - const gchar *quirk_str; + FuCsrDevice *self = FU_CSR_DEVICE (device); /* devices have to be whitelisted */ - quirk_str = fu_device_get_plugin_hints (FU_DEVICE (device)); - if (quirk_str == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported with this device"); - return FALSE; - } - if (g_strcmp0 (quirk_str, "require-delay") == 0) { - fu_csr_device_set_quirks (FU_CSR_DEVICE (device), - FU_CSR_DEVICE_QUIRK_REQUIRE_DELAY); - } + if (fu_device_has_custom_flag (FU_DEVICE (device), + FU_CSR_DEVICE_FLAG_REQUIRE_DELAY)) + self->quirks = FU_CSR_DEVICE_QUIRK_REQUIRE_DELAY; /* hardcoded */ fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); @@ -529,7 +527,6 @@ static gboolean fu_csr_device_open (FuUsbDevice *device, GError **error) { - FuCsrDevice *self = FU_CSR_DEVICE (device); GUsbDevice *usb_device = fu_usb_device_get_dev (device); /* open device and clear status */ @@ -539,6 +536,16 @@ g_prefix_error (error, "failed to claim HID interface: "); return FALSE; } + + /* success */ + return TRUE; +} + +static gboolean +fu_csr_device_setup (FuDevice *device, GError **error) +{ + FuCsrDevice *self = FU_CSR_DEVICE (device); + if (!fu_csr_device_clear_status (self, error)) return FALSE; @@ -576,18 +583,18 @@ klass_device->to_string = fu_csr_device_to_string; klass_device->write_firmware = fu_csr_device_download; klass_device->read_firmware = fu_csr_device_upload; + klass_device->prepare_firmware = fu_csr_device_prepare_firmware; klass_device->attach = fu_csr_device_attach; + klass_device->setup = fu_csr_device_setup; klass_usb_device->open = fu_csr_device_open; klass_usb_device->close = fu_csr_device_close; klass_usb_device->probe = fu_csr_device_probe; } FuCsrDevice * -fu_csr_device_new (GUsbDevice *usb_device) +fu_csr_device_new (FuUsbDevice *device) { - FuCsrDevice *device = NULL; - device = g_object_new (FU_TYPE_CSR_DEVICE, - "usb-device", usb_device, - NULL); - return device; + FuCsrDevice *self = g_object_new (FU_TYPE_CSR_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; } diff -Nru fwupd-1.0.9/plugins/csr/fu-csr-device.h fwupd-1.2.10/plugins/csr/fu-csr-device.h --- fwupd-1.0.9/plugins/csr/fu-csr-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/fu-csr-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_CSR_DEVICE_H -#define __FU_CSR_DEVICE_H - -#include -#include +#pragma once #include "fu-plugin.h" @@ -18,16 +13,6 @@ #define FU_TYPE_CSR_DEVICE (fu_csr_device_get_type ()) G_DECLARE_FINAL_TYPE (FuCsrDevice, fu_csr_device, FU, CSR_DEVICE, FuUsbDevice) -typedef enum { - FU_CSR_DEVICE_QUIRK_NONE = 0, - FU_CSR_DEVICE_QUIRK_REQUIRE_DELAY = (1 << 0), - FU_CSR_DEVICE_QUIRK_LAST -} FuCsrDeviceQuirks; - -FuCsrDevice *fu_csr_device_new (GUsbDevice *usb_device); -void fu_csr_device_set_quirks (FuCsrDevice *self, - FuCsrDeviceQuirks quirks); +FuCsrDevice *fu_csr_device_new (FuUsbDevice *device); G_END_DECLS - -#endif /* __FU_CSR_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/csr/fu-plugin-csr.c fwupd-1.2.10/plugins/csr/fu-plugin-csr.c --- fwupd-1.0.9/plugins/csr/fu-plugin-csr.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/fu-plugin-csr.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,22 +6,28 @@ #include "config.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" #include "fu-csr-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.qualcomm.dfu"); +} + gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) { - g_autoptr(FuCsrDevice) device = NULL; + g_autoptr(FuCsrDevice) dev = NULL; g_autoptr(FuDeviceLocker) locker = NULL; - device = fu_csr_device_new (usb_device); - fu_device_set_quirks (FU_DEVICE (device), fu_plugin_get_quirks (plugin)); - locker = fu_device_locker_new (device, error); + dev = fu_csr_device_new (device); + locker = fu_device_locker_new (dev, error); if (locker == NULL) return FALSE; - fu_plugin_device_add (plugin, FU_DEVICE (device)); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } @@ -60,7 +65,7 @@ locker = fu_device_locker_new (device, error); if (locker == NULL) return FALSE; - if (!fu_device_write_firmware (device, blob_fw, error)) + if (!fu_device_write_firmware (device, blob_fw, flags, error)) return FALSE; return fu_device_attach (device, error); } diff -Nru fwupd-1.0.9/plugins/csr/meson.build fwupd-1.2.10/plugins/csr/meson.build --- fwupd-1.0.9/plugins/csr/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,13 +1,11 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginCsr"'] -install_data([ - 'csr-aiaiai-h05.quirk', - 'csr-aiaiai-h60.quirk', - ], +install_data(['csr-aiaiai.quirk'], install_dir: join_paths(datadir, 'fwupd', 'quirks.d') ) shared_module('fu_plugin_csr', + fu_hash, sources : [ 'fu-csr-device.c', 'fu-plugin-csr.c', @@ -25,6 +23,7 @@ plugin_deps, ], link_with : [ + libfwupdprivate, dfu, ], ) diff -Nru fwupd-1.0.9/plugins/csr/README.md fwupd-1.2.10/plugins/csr/README.md --- fwupd-1.0.9/plugins/csr/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/csr/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/dell/dell.quirk fwupd-1.2.10/plugins/dell/dell.quirk --- fwupd-1.0.9/plugins/dell/dell.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/dell.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -1,8 +1,40 @@ -[fwupd-uefi-version-format] +# Realtek NIC in Dell docks +[DeviceInstanceId=USB\VID_0BDA&PID_8153] +Plugin = dell -# Dell & Alienware use AA.BB.CC.DD rather than AA.BB.CCDD -Dell Inc.=none -Alienware=none +# Dell TB16/TB18 cable +[Guid=TBT-00d4b051] +Plugin = thunderbolt +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 -[fwupd-daemon-version-format] -com.dell.uefi*.firmware=none +# Dell TB16/TB18 dock +[Guid=TBT-00d4b054] +Plugin = thunderbolt +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +# Dell WD15 dock +[Guid=MST-wd15-vmm3332-274] +Plugin = synapticsmst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +# Dell TB16 dock +[Guid=MST-tb16-vmm3320-274] +Plugin = synapticsmst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 +[Guid=MST-tb16-vmm3330-274] +Plugin = synapticsmst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +#Dell TB18 dock +[Guid=MST-tb18-vmm3320-274] +Plugin = synapticsmst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 +[Guid=MST-tb18-vmm3330-274] +Plugin = synapticsmst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +[SmbiosManufacturer=Dell Inc.] +UefiVersionFormat = quad + +[SmbiosManufacturer=Alienware] +UefiVersionFormat = quad diff -Nru fwupd-1.0.9/plugins/dell/fu-dell-smi.c fwupd-1.2.10/plugins/dell/fu-dell-smi.c --- fwupd-1.0.9/plugins/dell/fu-dell-smi.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/fu-dell-smi.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,13 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Mario Limonciello +/* + * Copyright (C) 2017 Mario Limonciello * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" -#include #include "fu-dell-smi.h" /* These are for dock query capabilities */ diff -Nru fwupd-1.0.9/plugins/dell/fu-dell-smi.h fwupd-1.2.10/plugins/dell/fu-dell-smi.h --- fwupd-1.0.9/plugins/dell/fu-dell-smi.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/fu-dell-smi.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Mario Limonciello +/* + * Copyright (C) 2017 Mario Limonciello * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_DELL_COMMON_H -#define __FU_DELL_COMMON_H +#pragma once #include "fu-device.h" #include @@ -127,5 +125,3 @@ /* VID/PID of ethernet controller on dock */ #define DOCK_NIC_VID 0x0bda #define DOCK_NIC_PID 0x8153 - -#endif /* __FU_DELL_COMMON_H */ diff -Nru fwupd-1.0.9/plugins/dell/fu-plugin-dell.c fwupd-1.2.10/plugins/dell/fu-plugin-dell.c --- fwupd-1.0.9/plugins/dell/fu-plugin-dell.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/fu-plugin-dell.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,21 +1,19 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes - * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2016 Mario Limonciello * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" -#include -#include #include #include #include #include #include +#include "fwupd-common.h" #include "fu-plugin-dell.h" #include "fu-plugin-vfuncs.h" #include "fu-device-metadata.h" @@ -47,6 +45,16 @@ 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" @@ -56,6 +64,8 @@ #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" @@ -108,13 +118,6 @@ 0x22, /* embedded PC */}; /** - * System blacklist on older libsmbios - */ -static guint16 system_blacklist [] = { 0x071E, /* latitude 5414 */ - 0x07A8, /* latitude 5580 */ - 0x077A, /* xps 9365 */ }; - -/** * Systems containing host MST device */ static guint16 systems_host_mst [] = { 0x062d, /* Latitude E7250 */ @@ -132,17 +135,6 @@ 0x071e, /* Latitude Rugged 5414 */ 0x071c, /* Latitude Rugged 7414 */}; -static void -_fwup_resource_iter_free (fwup_resource_iter *iter) -{ - fwup_resource_iter_destroy (&iter); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC (fwup_resource_iter, _fwup_resource_iter_free); -#pragma clang diagnostic pop - static guint16 fu_dell_get_system_id (FuPlugin *plugin) { @@ -177,11 +169,11 @@ static gboolean fu_dell_supported (FuPlugin *plugin) { - FuPluginData *data = fu_plugin_get_data (plugin); GBytes *de_table = NULL; + GBytes *da_table = NULL; GBytes *enclosure = NULL; - guint16 system_id = 0; const guint8 *value; + const struct da_structure *da_values; gsize len; /* make sure that Dell SMBIOS methods are available */ @@ -193,17 +185,16 @@ return FALSE; if (*value != 0xDE) return FALSE; - - /* skip blacklisted hw on libsmbios 2.3 or less */ - if (data->libsmbios_major <= 2 && - data->libsmbios_minor <= 3) { - system_id = fu_dell_get_system_id (plugin); - if (system_id == 0) - return FALSE; - for (guint i = 0; i < G_N_ELEMENTS (system_blacklist); i++) { - if (system_blacklist[i] == system_id) - 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 */ @@ -236,6 +227,8 @@ {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++) { @@ -266,39 +259,24 @@ data->can_switch_modes = TRUE; } -static AsVersionParseFlag +static FwupdVersionFormat fu_plugin_dell_get_version_format (FuPlugin *plugin) { const gchar *content; const gchar *quirk; + g_autofree gchar *group = NULL; content = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); if (content == NULL) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + return FWUPD_VERSION_FORMAT_TRIPLET; /* any quirks match */ - quirk = fu_plugin_lookup_quirk_by_id (plugin, - FU_QUIRKS_UEFI_VERSION_FORMAT, - content); - if (g_strcmp0 (quirk, "none") == 0) - return AS_VERSION_PARSE_FLAG_NONE; - - /* fall back */ - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; -} - -static gchar * -fu_plugin_get_dock_key (FuPlugin *plugin, - GUsbDevice *device, const gchar *guid) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - const gchar* platform_id; - - if (data->smi_obj->fake_smbios) - platform_id = "fake"; - else - platform_id = g_usb_device_get_platform_id (device); - return g_strdup_printf ("%s_%s", platform_id, guid); + group = g_strdup_printf ("SmbiosManufacturer=%s", content); + quirk = fu_plugin_lookup_quirk_by_id (plugin, group, + FU_QUIRKS_UEFI_VERSION_FORMAT); + if (quirk == NULL) + return FWUPD_VERSION_FORMAT_TRIPLET; + return fwupd_version_format_from_string (quirk); } static gboolean @@ -310,13 +288,12 @@ } static gboolean -fu_plugin_dock_node (FuPlugin *plugin, GUsbDevice *device, +fu_plugin_dock_node (FuPlugin *plugin, const gchar *platform, guint8 type, const gchar *component_guid, - const gchar *component_desc, const gchar *version) + const gchar *component_desc, const gchar *version, + FwupdVersionFormat version_format) { const gchar *dock_type; - g_autofree gchar *dock_id = NULL; - g_autofree gchar *dock_key = NULL; g_autofree gchar *dock_name = NULL; g_autoptr(FuDevice) dev = NULL; @@ -326,14 +303,9 @@ return FALSE; } - dock_key = fu_plugin_get_dock_key (plugin, device, component_guid); - if (fu_plugin_cache_lookup (plugin, dock_key) != NULL) { - g_debug ("%s is already registered.", dock_key); - return FALSE; - } - dev = fu_device_new (); - dock_id = g_strdup_printf ("DELL-%s" G_GUINT64_FORMAT, component_guid); + fu_device_set_physical_id (dev, platform); + fu_device_set_logical_id (dev, component_guid); if (component_desc != NULL) { dock_name = g_strdup_printf ("Dell %s %s", dock_type, component_desc); @@ -341,9 +313,9 @@ } else { dock_name = g_strdup_printf ("Dell %s", dock_type); } - fu_device_set_id (dev, dock_id); fu_device_set_vendor (dev, "Dell Inc."); fu_device_set_name (dev, dock_name); + fu_device_set_metadata (dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND, "device-firmware"); if (type == DOCK_TYPE_TB16) { fu_device_set_summary (dev, "A Thunderbolt™ 3 docking station"); } else if (type == DOCK_TYPE_WD15) { @@ -353,31 +325,33 @@ fu_device_add_guid (dev, component_guid); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); if (version != NULL) { - fu_device_set_version (dev, version); + fu_device_set_version (dev, version, version_format); if (fu_plugin_dell_capsule_supported (plugin)) { fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } else { + fu_device_set_update_error (dev, + "UEFI capsule updates turned off in BIOS setup"); } } - fu_plugin_device_add (plugin, dev); - fu_plugin_cache_add (plugin, dock_key, dev); + fu_plugin_device_register (plugin, dev); return TRUE; } - -void -fu_plugin_dell_device_added_cb (GUsbContext *ctx, - GUsbDevice *device, - FuPlugin *plugin) +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, + FuUsbDevice *device, + GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); - AsVersionParseFlag parse_flags; + FwupdVersionFormat version_format; guint16 pid; guint16 vid; const gchar *query_str; const gchar *component_guid = NULL; const gchar *component_name = NULL; + const gchar *platform; DOCK_UNION buf; DOCK_INFO *dock_info; gboolean old_ec = FALSE; @@ -386,27 +360,33 @@ /* don't look up immediately if a dock is connected as that would mean a SMI on every USB device that showed up on the system */ if (!data->smi_obj->fake_smbios) { - vid = g_usb_device_get_vid (device); - pid = g_usb_device_get_pid (device); + vid = fu_usb_device_get_vid (device); + pid = fu_usb_device_get_pid (device); + platform = fu_device_get_physical_id (FU_DEVICE (device)); } else { vid = data->fake_vid; pid = data->fake_pid; + platform = "fake"; } /* we're going to match on the Realtek NIC in the dock */ - if (vid != DOCK_NIC_VID || pid != DOCK_NIC_PID) - return; + if (vid != DOCK_NIC_VID || pid != DOCK_NIC_PID) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, + "wrong VID/PID %04x:%04x", vid, pid); + return FALSE; + } buf.buf = NULL; if (!fu_dell_query_dock (data->smi_obj, &buf)) { - g_debug ("No dock detected."); - return; + g_debug ("no dock detected"); + return TRUE; } if (buf.record->dock_info_header.dir_version != 1) { - g_debug ("Dock info header version unknown: %d", - buf.record->dock_info_header.dir_version); - return; + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, + "dock info header version unknown %d", + buf.record->dock_info_header.dir_version); + return FALSE; } dock_info = &buf.record->dock_info; @@ -418,7 +398,7 @@ 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; @@ -432,15 +412,20 @@ query_str = g_strrstr (dock_info->components[i].description, "Query "); if (query_str == NULL) { - g_debug ("Invalid dock component request"); - return; + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, + "invalid dock component request"); + return FALSE; } if (!fu_plugin_dell_match_dock_component (query_str + 6, &component_guid, &component_name)) { - g_debug ("Unable to match dock component %s", - query_str); - return; + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, + "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 */ @@ -456,79 +441,42 @@ 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, - device, - buf.record->dock_info_header.dock_type, - component_guid, - component_name, - fw_str)) { - g_debug ("Failed to create %s", component_name); - return; + platform, + buf.record->dock_info_header.dock_type, + component_guid, + component_name, + fw_str, + version_format)) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "failed to create %s", component_name); + return FALSE; } } /* 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, - device, + platform, buf.record->dock_info_header.dock_type, DOCK_FLASH_GUID, NULL, - flash_ver_str)) { - g_debug ("Failed to create top dock node"); - return; - } - -#if defined (HAVE_SYNAPTICS) - fu_plugin_request_recoldplug (plugin); -#endif -} - -void -fu_plugin_dell_device_removed_cb (GUsbContext *ctx, - GUsbDevice *device, - FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - const gchar *guids[] = { WD15_EC_GUID, TB16_EC_GUID, TB16_PC2_GUID, - TB16_PC1_GUID, WD15_PC1_GUID, - LEGACY_CBL_GUID, UNIV_CBL_GUID, - TBT_CBL_GUID, DOCK_FLASH_GUID}; - guint16 pid; - guint16 vid; - FuDevice *dev = NULL; + flash_ver_str, + version_format)) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "failed to create top dock node"); - if (!data->smi_obj->fake_smbios) { - vid = g_usb_device_get_vid (device); - pid = g_usb_device_get_pid (device); - } else { - vid = data->fake_vid; - pid = data->fake_pid; + return FALSE; } - /* we're going to match on the Realtek NIC in the dock */ - if (vid != DOCK_NIC_VID || pid != DOCK_NIC_PID) - return; - - /* remove any components already in database? */ - for (guint i = 0; i < G_N_ELEMENTS (guids); i++) { - g_autofree gchar *dock_key = NULL; - dock_key = fu_plugin_get_dock_key (plugin, device, - guids[i]); - dev = fu_plugin_cache_lookup (plugin, dock_key); - if (dev != NULL) { - fu_plugin_device_remove (plugin, - dev); - fu_plugin_cache_remove (plugin, dock_key); - } - } #if defined (HAVE_SYNAPTICS) fu_plugin_request_recoldplug (plugin); #endif + return TRUE; } gboolean @@ -554,7 +502,7 @@ if (completion_code[3] == DELL_SUCCESS) { fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS); } else { - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); + FwupdUpdateState update_state = FWUPD_UPDATE_STATE_FAILED; switch (completion_code[3]) { case DELL_CONSISTENCY_FAIL: tmp = "The image failed one or more consistency checks."; @@ -570,12 +518,15 @@ break; case DELL_BATTERY_MISSING: tmp = "A battery must be installed for the operation to complete."; + update_state = FWUPD_UPDATE_STATE_FAILED_TRANSIENT; break; case DELL_BATTERY_DEAD: tmp = "A fully-charged battery must be present for the operation to complete."; + update_state = FWUPD_UPDATE_STATE_FAILED_TRANSIENT; break; case DELL_AC_MISSING: tmp = "An external power adapter must be connected for the operation to complete."; + update_state = FWUPD_UPDATE_STATE_FAILED_TRANSIENT; break; case DELL_CANT_SET_12V: tmp = "The 12V required to program the flash-memory could not be set."; @@ -601,6 +552,7 @@ default: break; } + fu_device_set_update_state (device, update_state); if (tmp != NULL) fu_device_set_update_error (device, tmp); } @@ -682,17 +634,17 @@ } tpm_guid_raw = g_strdup_printf ("%04x-%s", system_id, tpm_mode); - tpm_guid = as_utils_guid_from_string (tpm_guid_raw); + tpm_guid = fwupd_guid_hash_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 = fwupd_guid_hash_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, + FWUPD_VERSION_FORMAT_QUAD); /* make it clear that the TPM is a discrete device of the product */ if (!data->smi_obj->fake_smbios) { @@ -708,21 +660,25 @@ fu_device_set_vendor (dev, "Dell Inc."); fu_device_set_name (dev, pretty_tpm_name); fu_device_set_summary (dev, "Platform TPM device"); - fu_device_set_version (dev, version_str); + fu_device_set_version (dev, version_str, FWUPD_VERSION_FORMAT_QUAD); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_icon (dev, "computer"); + fu_device_set_metadata (dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND, "dell-tpm-firmware"); if ((out->status & TPM_OWN_MASK) == 0 && out->flashes_left > 0) { if (fu_plugin_dell_capsule_supported (plugin)) { fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } else { + fu_device_set_update_error (dev, + "UEFI capsule updates turned off in BIOS setup"); } fu_device_set_flashes_left (dev, out->flashes_left); } else { g_debug ("%s updating disabled due to TPM ownership", pretty_tpm_name); } - fu_plugin_device_add (plugin, dev); + fu_plugin_device_register (plugin, dev); /* build alternate device node */ if (can_switch_modes) { @@ -736,7 +692,8 @@ fu_device_add_flag (dev_alt, FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag (dev_alt, FWUPD_DEVICE_FLAG_LOCKED); fu_device_add_icon (dev_alt, "computer"); - fu_device_set_alternate (dev_alt, dev); + fu_device_set_alternate_id (dev_alt, fu_device_get_id (dev)); + fu_device_set_metadata (dev_alt, FU_DEVICE_METADATA_UEFI_DEVICE_KIND, "dell-tpm-firmware"); fu_device_add_parent_guid (dev_alt, tpm_guid); /* If TPM is not owned and at least 1 flash left allow mode switching @@ -750,7 +707,7 @@ g_debug ("%s mode switch disabled due to TPM ownership", pretty_tpm_name); } - fu_plugin_device_add (plugin, dev_alt); + fu_plugin_device_register (plugin, dev_alt); } else g_debug ("System %04x does not offer TPM modeswitching", @@ -759,170 +716,6 @@ return TRUE; } -gboolean -fu_plugin_unlock (FuPlugin *plugin, FuDevice *device, GError **error) -{ - FuDevice *device_alt = NULL; - FwupdDeviceFlags device_flags_alt = 0; - guint flashes_left = 0; - guint flashes_left_alt = 0; - - /* for unlocking TPM1.2 <-> TPM2.0 switching */ - g_debug ("Unlocking upgrades for: %s (%s)", fu_device_get_name (device), - fu_device_get_id (device)); - device_alt = fu_device_get_alternate (device); - if (device_alt == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "No alternate device for %s", - fu_device_get_name (device)); - return FALSE; - } - g_debug ("Preventing upgrades for: %s (%s)", fu_device_get_name (device_alt), - fu_device_get_id (device_alt)); - - flashes_left = fu_device_get_flashes_left (device); - flashes_left_alt = fu_device_get_flashes_left (device_alt); - if (flashes_left == 0) { - /* flashes left == 0 on both means no flashes left */ - if (flashes_left_alt == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "ERROR: %s has no flashes left.", - fu_device_get_name (device)); - /* flashes left == 0 on just unlocking device is ownership */ - } else { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "ERROR: %s is currently OWNED. " - "Ownership must be removed to switch modes.", - fu_device_get_name (device_alt)); - } - return FALSE; - } - - - /* clone the info from real device but prevent it from being flashed */ - device_flags_alt = fu_device_get_flags (device_alt); - fu_device_set_flags (device, device_flags_alt); - fu_device_set_flags (device_alt, device_flags_alt & ~FWUPD_DEVICE_FLAG_UPDATABLE); - - /* make sure that this unlocked device can be updated */ - fu_device_set_version (device, "0.0.0.0"); - - return TRUE; -} - -gboolean -fu_plugin_update (FuPlugin *plugin, - FuDevice *device, - GBytes *blob_fw, - FwupdInstallFlags flags, - GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - g_autoptr (fwup_resource_iter) iter = NULL; - fwup_resource *re = NULL; - const gchar *name = NULL; - gint rc; - guint flashes_left; - const gchar *guidstr = NULL; - efi_guid_t guid; - - /* test the flash counter - * - devices with 0 left at setup aren't allowed offline updates - * - devices greater than 0 should show a warning when near 0 - */ - flashes_left = fu_device_get_flashes_left (device); - if (flashes_left > 0) { - name = fu_device_get_name (device); - g_debug ("%s has %u flashes left", name, flashes_left); - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && - flashes_left <= 2) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "WARNING: %s only has %u flashes left. " - "See https://github.com/hughsie/fwupd/wiki/Dell-TPM:-flashes-left for more information.", - name, flashes_left); - return FALSE; - } - } - - if (data->smi_obj->fake_smbios) - return TRUE; - - /* perform the update */ - g_debug ("Performing capsule update"); - - /* Stuff the payload into a different GUID - * - with fwup 0.5 this uses the ESRT GUID - * - with fwup 0.6 this uses the payload's GUID - * it's preferable to use payload GUID to avoid - * a corner case scenario of UEFI BIOS and non-ESRT - * update happening at same time - */ - fwup_resource_iter_create (&iter); - fwup_resource_iter_next (iter, &re); - guidstr = fu_device_get_guid_default (device); - rc = efi_str_to_guid (guidstr, &guid); - if (rc < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Failed to convert guid to string"); - return FALSE; - } - rc = fwup_set_guid (iter, &re, &guid); - if (rc < 0 || re == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Failed to update GUID %s", - strerror (rc)); - return FALSE; - } - - /* NOTE: if there are problems with this working, adjust the - * GUID in the capsule header to match something in ESRT. - * This won't actually cause any bad behavior because the real - * payload GUID is extracted later on. - */ - fu_device_set_status (device, FWUPD_STATUS_SCHEDULING); - rc = fwup_set_up_update_with_buf (re, 0, - g_bytes_get_data (blob_fw, NULL), - g_bytes_get_size (blob_fw)); - if (rc < 0) { - g_autoptr(GString) err_string = g_string_new ("Dell firmware update failed:\n"); - - rc = 1; - for (int i = 0; rc > 0; i++) { - char *filename = NULL; - char *function = NULL; - char *message = NULL; - int line = 0; - int err = 0; - - rc = efi_error_get (i, &filename, &function, &line, &message, &err); - if (rc <= 0) - break; - g_string_append_printf (err_string, - "{error #%d} %s:%d %s(): %s: %s \n", - i, filename, line, function, message, strerror(err)); - } - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "%s", - err_string->str); - return FALSE; - } - return TRUE; -} - void fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) { @@ -943,7 +736,7 @@ device_id = g_strdup_printf ("TBT-%04x%04x", 0x00d4u, (unsigned) system_id); fu_device_set_vendor_id (device, vendor_id); - fu_device_add_guid (device, device_id); + fu_device_add_instance_id (device, device_id); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); } } @@ -996,6 +789,7 @@ gboolean fu_plugin_update_prepare (FuPlugin *plugin, + FwupdInstallFlags flags, FuDevice *device, GError **error) { @@ -1005,6 +799,7 @@ gboolean fu_plugin_update_cleanup (FuPlugin *plugin, + FwupdInstallFlags flags, FuDevice *device, GError **error) { @@ -1029,13 +824,12 @@ FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); g_autofree gchar *tmp = NULL; - data->libsmbios_major = smbios_get_library_version_major(); - data->libsmbios_minor = smbios_get_library_version_minor(); - g_debug ("Using libsmbios %u.%u", data->libsmbios_major, - data->libsmbios_minor); - tmp = g_strdup_printf ("%u.%u", data->libsmbios_major, - data->libsmbios_minor); + 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()); fu_plugin_add_runtime_version (plugin, "com.dell.libsmbios", tmp); + g_debug ("Using libsmbios %s", tmp); data->smi_obj = g_malloc0 (sizeof (FuDellSmiObj)); if (g_getenv ("FWUPD_DELL_VERBOSE") != NULL) @@ -1045,6 +839,10 @@ data->smi_obj->fake_smbios = FALSE; if (g_getenv ("FWUPD_DELL_FAKE_SMBIOS") != NULL) data->smi_obj->fake_smbios = TRUE; + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + + /* make sure that UEFI plugin is ready to receive devices */ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "uefi"); } void @@ -1060,8 +858,8 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); - GUsbContext *usb_ctx = fu_plugin_get_usb_context (plugin); - gint uefi_supported; + g_autofree gchar *sysfsfwdir = NULL; + g_autofree gchar *esrtdir = NULL; if (data->smi_obj->fake_smbios) { g_debug ("Called with fake SMBIOS implementation. " @@ -1087,26 +885,16 @@ } /* If ESRT is not turned on, fwupd will have already created an - * unlock device (if compiled with support). + * unlock device. * * Once unlocked, that will enable flashing capsules here too. - * - * that means we should only look for supported = 1 */ - uefi_supported = fwup_supported (); - data->capsule_supported = (uefi_supported == 1); - if (!data->capsule_supported) { - g_debug ("UEFI capsule firmware updating not supported (%x)", - (guint) uefi_supported); - } - - if (usb_ctx != NULL) { - g_signal_connect (usb_ctx, "device-added", - G_CALLBACK (fu_plugin_dell_device_added_cb), - plugin); - g_signal_connect (usb_ctx, "device-removed", - G_CALLBACK (fu_plugin_dell_device_removed_cb), - plugin); + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + esrtdir = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); + if (g_file_test (esrtdir, G_FILE_TEST_EXISTS)) { + data->capsule_supported = TRUE; + } else { + g_debug ("UEFI capsule firmware updating not supported"); } return TRUE; diff -Nru fwupd-1.0.9/plugins/dell/fu-plugin-dell.h fwupd-1.2.10/plugins/dell/fu-plugin-dell.h --- fwupd-1.0.9/plugins/dell/fu-plugin-dell.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/fu-plugin-dell.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016 Mario Limonciello +/* + * Copyright (C) 2016 Mario Limonciello * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_PLUGIN_DELL_H -#define __FU_PLUGIN_DELL_H +#pragma once -#include #include "fu-plugin.h" #include "fu-dell-smi.h" @@ -18,8 +15,6 @@ guint16 fake_pid; gboolean can_switch_modes; gboolean capsule_supported; - guint libsmbios_major; - guint libsmbios_minor; }; void @@ -30,16 +25,6 @@ gboolean fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error); -void -fu_plugin_dell_device_added_cb (GUsbContext *ctx, - GUsbDevice *device, - FuPlugin *plugin); - -void -fu_plugin_dell_device_removed_cb (GUsbContext *ctx, - GUsbDevice *device, - FuPlugin *plugin); - /* These are nodes that will indicate information about * the TPM status */ @@ -54,5 +39,3 @@ #define TPM_TYPE_MASK 0x0F00 #define TPM_1_2_MODE 0x0001 #define TPM_2_0_MODE 0x0002 - -#endif /* __FU_PLUGIN_DELL_H */ diff -Nru fwupd-1.0.9/plugins/dell/fu-self-test.c fwupd-1.2.10/plugins/dell/fu-self-test.c --- fwupd-1.0.9/plugins/dell/fu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -8,55 +7,110 @@ #include "config.h" #include -#include #include #include +#include "fu-device-private.h" #include "fu-plugin-private.h" #include "fu-plugin-dell.h" +#include "fu-plugin-vfuncs.h" + +static FuDevice * +_find_device_by_id (GPtrArray *devices, const gchar *device_id) +{ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + if (g_strcmp0 (fu_device_get_id (device), device_id) == 0) + return device; + } + return NULL; +} + +static FuDevice * +_find_device_by_name (GPtrArray *devices, const gchar *device_id) +{ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + if (g_strcmp0 (fu_device_get_name (device), device_id) == 0) + return device; + } + return NULL; +} static void _plugin_device_added_cb (FuPlugin *plugin, FuDevice *device, gpointer user_data) { - FuDevice **dev = (FuDevice **) user_data; - g_set_object (dev, device); + GPtrArray *devices = (GPtrArray *) user_data; + if (fu_device_get_alternate_id (device) != NULL) { + FuDevice *device_alt = _find_device_by_id (devices, fu_device_get_alternate_id (device)); + if (device_alt != NULL) + fu_device_set_alternate (device, device_alt); + } + g_ptr_array_add (devices, g_object_ref (device)); +} + +static void +fu_engine_plugin_device_register_cb (FuPlugin *plugin_dell, + FuDevice *device, + gpointer user_data) +{ + FuPlugin *plugin_uefi = FU_PLUGIN (user_data); + g_autofree gchar *dbg = fu_device_to_string (device); + g_debug ("registering device: %s", dbg); + fu_plugin_runner_device_register (plugin_uefi, device); } static void fu_plugin_dell_tpm_func (void) { + FuDevice *device_v12; + FuDevice *device_v20; + const guint8 fw[30] = { 'F', 'W', 0x00 }; gboolean ret; struct tpm_status tpm_out; - FuDevice *device_alt = NULL; + g_autoptr(FuPlugin) plugin_dell = NULL; + g_autoptr(FuPlugin) plugin_uefi = NULL; + g_autoptr(GBytes) blob_fw = g_bytes_new_static (fw, sizeof(fw)); g_autoptr(GError) error = NULL; - g_autoptr(FuDevice) device = NULL; - g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(GPtrArray) devices = NULL; memset (&tpm_out, 0x0, sizeof(tpm_out)); - g_setenv ("FWUPD_DELL_FAKE_SMBIOS", "1", FALSE); - plugin = fu_plugin_new (); - ret = fu_plugin_open (plugin, PLUGINBUILDDIR "/libfu_plugin_dell.so", &error); + plugin_uefi = fu_plugin_new (); + ret = fu_plugin_open (plugin_uefi, PLUGINBUILDDIR "/../uefi/libfu_plugin_uefi.so", &error); g_assert_no_error (error); g_assert (ret); - ret = fu_plugin_runner_startup (plugin, &error); + ret = fu_plugin_runner_startup (plugin_uefi, &error); g_assert_no_error (error); g_assert (ret); - ret = fu_plugin_runner_coldplug(plugin, &error); - g_signal_connect (plugin, "device-added", + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_signal_connect (plugin_uefi, "device-added", G_CALLBACK (_plugin_device_added_cb), - &device); + devices); + + plugin_dell = fu_plugin_new (); + ret = fu_plugin_open (plugin_dell, PLUGINBUILDDIR "/libfu_plugin_dell.so", &error); + g_assert_no_error (error); + g_assert (ret); + ret = fu_plugin_runner_startup (plugin_dell, &error); + g_assert_no_error (error); + g_assert (ret); + g_signal_connect (plugin_dell, "device-register", + G_CALLBACK (fu_engine_plugin_device_register_cb), + plugin_uefi); + ret = fu_plugin_runner_coldplug (plugin_dell, &error); g_assert_no_error (error); g_assert (ret); /* inject fake data (no TPM) */ tpm_out.ret = -2; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, FALSE); - ret = fu_plugin_dell_detect_tpm (plugin, &error); + ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); g_assert_no_error (error); - g_assert (!ret); + g_assert_false (ret); + g_assert_cmpint (devices->len, ==, 0); /* inject fake data: * - that is out of flashes @@ -68,64 +122,62 @@ tpm_out.fw_version = 0; tpm_out.status = TPM_EN_MASK | (TPM_1_2_MODE << 8); tpm_out.flashes_left = 0; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin, &error); - device_alt = fu_device_get_alternate (device); - g_assert_no_error (error); - g_assert (ret); - g_assert (device != NULL); - g_assert (device_alt != NULL); + ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); + g_assert_true (ret); + g_assert_cmpint (devices->len, ==, 2); /* make sure 2.0 is locked */ - g_assert_true (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)); + device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + g_assert_nonnull (device_v20); + g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_LOCKED)); /* make sure not allowed to flash 1.2 */ - g_assert_false (fu_device_has_flag (device_alt, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + g_assert_nonnull (device_v12); + g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); /* try to unlock 2.0 */ - ret = fu_plugin_runner_unlock (plugin, device, &error); + ret = fu_plugin_runner_unlock (plugin_uefi, device_v20, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); - g_assert (!ret); + g_assert_false (ret); g_clear_error (&error); /* cleanup */ - fu_plugin_device_remove (plugin, device_alt); - fu_plugin_device_remove (plugin, device); - g_clear_object (&device); + g_ptr_array_set_size (devices, 0); /* inject fake data: - * - that hasflashes + * - that has flashes * - owned * - TPM 1.2 * dev will be the locked 2.0, alt will be the orig 1.2 */ tpm_out.status = TPM_EN_MASK | TPM_OWN_MASK | (TPM_1_2_MODE << 8); tpm_out.flashes_left = 125; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin, &error); - device_alt = fu_device_get_alternate (device); + ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); g_assert_no_error (error); g_assert (ret); - g_assert (device != NULL); - g_assert (device_alt != NULL); /* make sure not allowed to flash 1.2 */ - g_assert_false (fu_device_has_flag (device_alt, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + g_assert_nonnull (device_v12); + g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); /* try to unlock 2.0 */ - ret = fu_plugin_runner_unlock (plugin, device, &error); + device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + g_assert_nonnull (device_v20); + ret = fu_plugin_runner_unlock (plugin_uefi, device_v20, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); - g_assert (!ret); + g_assert_false (ret); g_clear_error (&error); /* cleanup */ - fu_plugin_device_remove (plugin, device_alt); - fu_plugin_device_remove (plugin, device); - g_clear_object (&device); + g_ptr_array_set_size (devices, 0); /* inject fake data: * - that has flashes @@ -135,33 +187,32 @@ */ tpm_out.status = TPM_EN_MASK | (TPM_1_2_MODE << 8); tpm_out.flashes_left = 125; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin, &error); - device_alt = fu_device_get_alternate (device); + ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); g_assert_no_error (error); g_assert (ret); - g_assert (device != NULL); - g_assert (device_alt != NULL); /* make sure allowed to flash 1.2 but not 2.0 */ - g_assert_true (fu_device_has_flag (device_alt, FWUPD_DEVICE_FLAG_UPDATABLE)); - g_assert_false (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + g_assert_nonnull (device_v12); + g_assert_true (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + g_assert_nonnull (device_v20); + g_assert_false (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE)); /* try to unlock 2.0 */ - ret = fu_plugin_runner_unlock (plugin, device, &error); + ret = fu_plugin_runner_unlock (plugin_uefi, device_v20, &error); g_assert_no_error (error); g_assert (ret); /* make sure no longer allowed to flash 1.2 but can flash 2.0 */ - g_assert_false (fu_device_has_flag (device_alt, FWUPD_DEVICE_FLAG_UPDATABLE)); - g_assert_true (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE)); /* cleanup */ - fu_plugin_device_remove (plugin, device_alt); - fu_plugin_device_remove (plugin, device); - g_clear_object (&device); + g_ptr_array_set_size (devices, 0); /* inject fake data: * - that has 1 flash left @@ -171,37 +222,36 @@ */ tpm_out.status = TPM_EN_MASK | (TPM_2_0_MODE << 8); tpm_out.flashes_left = 1; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin, &error); - device_alt = fu_device_get_alternate (device); + ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); g_assert_no_error (error); g_assert (ret); - g_assert (device != NULL); - g_assert (device_alt != NULL); /* make sure allowed to flash 2.0 but not 1.2 */ - g_assert_true (fu_device_has_flag (device_alt, FWUPD_DEVICE_FLAG_UPDATABLE)); - g_assert_false (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + g_assert_nonnull (device_v20); + g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + g_assert_nonnull (device_v12); + g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); /* With one flash left we need an override */ - ret = fu_plugin_runner_update (plugin, device_alt, NULL, NULL, - FWUPD_INSTALL_FLAG_NONE, &error); - g_assert (!ret); + ret = fu_plugin_runner_update (plugin_uefi, device_v20, blob_fw, + FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false (ret); g_clear_error (&error); /* test override */ - ret = fu_plugin_runner_update (plugin, device_alt, NULL, NULL, - FWUPD_INSTALL_FLAG_FORCE, &error); - g_assert (ret); + g_test_expect_message ("FuPluginUefi", G_LOG_LEVEL_WARNING, + "missing or invalid embedded capsule header"); + ret = fu_plugin_runner_update (plugin_uefi, device_v20, blob_fw, + FWUPD_INSTALL_FLAG_FORCE, &error); + g_test_assert_expected_messages (); g_assert_no_error (error); - - /* cleanup */ - fu_plugin_device_remove (plugin, device_alt); - fu_plugin_device_remove (plugin, device); - g_clear_object (&device); + g_assert (ret); } static void @@ -212,40 +262,53 @@ DOCK_UNION buf; DOCK_INFO *dock_info; g_autoptr(GError) error = NULL; - g_autoptr(FuDevice) device = NULL; - g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(FuPlugin) plugin_uefi = fu_plugin_new (); + g_autoptr(FuPlugin) plugin_dell = fu_plugin_new (); - g_setenv ("FWUPD_DELL_FAKE_SMBIOS", "1", FALSE); - plugin = fu_plugin_new (); - ret = fu_plugin_open (plugin, PLUGINBUILDDIR "/libfu_plugin_dell.so", &error); + ret = fu_plugin_open (plugin_uefi, PLUGINBUILDDIR "/../uefi/libfu_plugin_uefi.so", &error); g_assert_no_error (error); g_assert (ret); - ret = fu_plugin_runner_startup (plugin, &error); + ret = fu_plugin_runner_startup (plugin_uefi, &error); g_assert_no_error (error); g_assert (ret); - g_signal_connect (plugin, "device-added", + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_signal_connect (plugin_uefi, "device-added", G_CALLBACK (_plugin_device_added_cb), - &device); - ret = fu_plugin_runner_coldplug (plugin, &error); + devices); + ret = fu_plugin_open (plugin_dell, PLUGINBUILDDIR "/libfu_plugin_dell.so", &error); + g_assert_no_error (error); + g_assert (ret); + ret = fu_plugin_runner_startup (plugin_dell, &error); + g_assert_no_error (error); + g_assert (ret); + g_signal_connect (plugin_dell, "device-register", + G_CALLBACK (fu_engine_plugin_device_register_cb), + plugin_uefi); + ret = fu_plugin_runner_coldplug (plugin_dell, &error); g_assert_no_error (error); g_assert (ret); /* make sure bad device doesn't trigger this */ - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &out, 0x1234, 0x4321, NULL, FALSE); - fu_plugin_dell_device_added_cb (NULL, NULL, plugin); - g_assert (device == NULL); + ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + g_assert_false (ret); + g_clear_error (&error); + g_assert_cmpint (devices->len, ==, 0); /* inject a USB dongle matching correct VID/PID */ out[0] = 0; out[1] = 0; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, NULL, FALSE); - fu_plugin_dell_device_added_cb (NULL, NULL, plugin); - g_assert (device == NULL); + ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + g_assert_true (ret); + g_clear_error (&error); + g_assert_cmpint (devices->len, ==, 0); /* inject valid TB16 dock w/ invalid flash pkg version */ buf.record = g_malloc0 (sizeof(DOCK_INFO_RECORD)); @@ -272,17 +335,15 @@ "Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", 44); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - fu_plugin_dell_device_added_cb (NULL, NULL, - plugin); - g_assert (device != NULL); - g_clear_object (&device); + ret = fu_plugin_usb_device_added (plugin_dell, NULL, NULL); + g_assert (ret); + g_assert_cmpint (devices->len, ==, 4); + g_ptr_array_set_size (devices, 0); g_free (buf.record); - fu_plugin_dell_device_removed_cb (NULL, NULL, - plugin); /* inject valid TB16 dock w/ older system EC */ buf.record = g_malloc0 (sizeof(DOCK_INFO_RECORD)); @@ -309,18 +370,15 @@ "Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", 44); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - fu_plugin_dell_device_added_cb (NULL, NULL, - plugin); - g_assert (device != NULL); - g_clear_object (&device); + ret = fu_plugin_usb_device_added (plugin_dell, NULL, NULL); + g_assert (ret); + g_assert_cmpint (devices->len, ==, 3); + g_ptr_array_set_size (devices, 0); g_free (buf.record); - fu_plugin_dell_device_removed_cb (NULL, NULL, - plugin); - /* inject valid WD15 dock w/ invalid flash pkg version */ buf.record = g_malloc0 (sizeof(DOCK_INFO_RECORD)); @@ -344,18 +402,16 @@ "Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", 43); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - fu_plugin_dell_device_added_cb (NULL, NULL, - plugin); - g_assert (device != NULL); - g_clear_object (&device); + ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + g_assert (ret); + g_assert_no_error (error); + g_assert_cmpint (devices->len, ==, 3); + g_ptr_array_set_size (devices, 0); g_free (buf.record); - fu_plugin_dell_device_removed_cb (NULL, NULL, - plugin); - /* inject valid WD15 dock w/ older system EC */ buf.record = g_malloc0 (sizeof(DOCK_INFO_RECORD)); @@ -379,17 +435,16 @@ "Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", 43); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - fu_plugin_dell_device_added_cb (NULL, NULL, - plugin); - g_assert (device != NULL); - g_clear_object (&device); + ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + g_assert (ret); + g_assert_no_error (error); + g_assert_cmpint (devices->len, ==, 2); + g_ptr_array_set_size (devices, 0); g_free (buf.record); - fu_plugin_dell_device_removed_cb (NULL, NULL, - plugin); /* inject an invalid future dock */ buf.record = g_malloc0 (sizeof(DOCK_INFO_RECORD)); @@ -407,21 +462,30 @@ "Dock1,EC,MIPS32,FUT_Dock,0 :Query 2 0 2 2 0", 43); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin, + fu_plugin_dell_inject_fake_data (plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - fu_plugin_dell_device_added_cb (NULL, NULL, - plugin); - g_assert (device == NULL); + ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + g_assert_false (ret); + g_assert_cmpint (devices->len, ==, 0); g_free (buf.record); } int main (int argc, char **argv) { + g_autofree gchar *sysfsdir = NULL; g_test_init (&argc, &argv, NULL); + /* change path */ + g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE); + + /* change behaviour */ + sysfsdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + g_setenv ("FWUPD_UEFI_ESP_PATH", sysfsdir, TRUE); + g_setenv ("FWUPD_DELL_FAKE_SMBIOS", "1", FALSE); + /* only critical and error are fatal */ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); diff -Nru fwupd-1.0.9/plugins/dell/meson.build fwupd-1.2.10/plugins/dell/meson.build --- fwupd-1.0.9/plugins/dell/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -5,6 +5,7 @@ ) shared_module('fu_plugin_dell', + fu_hash, sources : [ 'fu-plugin-dell.c', 'fu-dell-smi.c', @@ -16,22 +17,27 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : [ - cargs, - ], + cargs, + ], dependencies : [ plugin_deps, efivar, libsmbios_c, - fwup, ], ) if get_option('tests') + testdatadir = join_paths(meson.current_source_dir(), 'tests') + cargs += '-DTESTDATADIR="' + testdatadir + '"' cargs += '-DFU_OFFLINE_DESTDIR="/tmp/fwupd-self-test"' cargs += '-DPLUGINBUILDDIR="' + meson.current_build_dir() + '"' e = executable( 'dell-self-test', + fu_hash, sources : [ 'fu-self-test.c', 'fu-dell-smi.c', @@ -45,14 +51,11 @@ dependencies : [ plugin_deps, efivar, - fwup, sqlite, - gudev, libsmbios_c, valgrind, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : [ diff -Nru fwupd-1.0.9/plugins/dell/README.md fwupd-1.2.10/plugins/dell/README.md --- fwupd-1.0.9/plugins/dell/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -6,24 +6,32 @@ 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 ------------------ -For Dell support you will need libsmbios_c version 2.3.0 or later and -efivar. -* source: http://linux.dell.com/cgi-bin/cgit.cgi/libsmbios.git/ -* rpms: https://apps.fedoraproject.org/packages/libsmbios -* debs (Debian): http://tracker.debian.org/pkg/libsmbios -* debs (Ubuntu): http://launchpad.net/ubuntu/+source/libsmbios +For Dell support you will need libsmbios_c version 2.4.0 or later. + +* source: https://github.com/dell/libsmbios +* binaries: https://github.com/dell/libsmbios/releases If you don't want or need this functionality you can use the -`--disable-dell` option. +`-Dplugin_dell=false` option. # Devices powered by the Dell Plugin The Dell plugin creates device nodes for PC's that have switchable TPMs as well as the Type-C docks (WD15/TB16). -These device nodes can be flashed using UEFI capsule (and fwupdate) but don't +These device nodes can be flashed using UEFI capsule but don't use the ESRT table to communicate device status or version information. This is intentional behavior because more complicated decisions need to be made Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/image and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/image differ diff -Nru fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/status fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/status --- fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/type fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/type --- fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/version fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/version --- fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/xoffset fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/xoffset --- fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/xoffset 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/xoffset 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +123 diff -Nru fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/yoffset fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/yoffset --- fwupd-1.0.9/plugins/dell/tests/acpi/bgrt/yoffset 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/acpi/bgrt/yoffset 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +456 Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/dell/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/dell/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 differ diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c fwupd-1.2.10/plugins/dell/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c --- fwupd-1.0.9/plugins/dell/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 \ No newline at end of file diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/capsule_flags fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/capsule_flags --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/capsule_flags 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/capsule_flags 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0xfe diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/fw_class fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/fw_class --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/fw_class 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/fw_class 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +ddc0ee61-e7f0-4e7d-acc5-c070a398838e diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/fw_type fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/fw_type --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/fw_type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/fw_type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/fw_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/fw_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +65586 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_status fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_status --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/last_attempt_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +18472960 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/lowest_supported_fw_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/lowest_supported_fw_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry0/lowest_supported_fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry0/lowest_supported_fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +65582 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/capsule_flags fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/capsule_flags --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/capsule_flags 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/capsule_flags 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0x8010 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/fw_class fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/fw_class --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/fw_class 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/fw_class 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +671d19d0-d43c-4852-98d9-1ce16f9967e4 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/fw_type fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/fw_type --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/fw_type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/fw_type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +2 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/fw_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/fw_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +3090287969 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_status fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_status --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/last_attempt_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/lowest_supported_fw_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/lowest_supported_fw_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry1/lowest_supported_fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry1/lowest_supported_fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/capsule_flags fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/capsule_flags --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/capsule_flags 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/capsule_flags 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0x8010 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/fw_class fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/fw_class --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/fw_class 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/fw_class 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +00000000-0000-0000-0000-000000000000 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/fw_type fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/fw_type --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/fw_type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/fw_type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +2 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/fw_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/fw_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +3090287969 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_status fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_status --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/last_attempt_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/lowest_supported_fw_version fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/lowest_supported_fw_version --- fwupd-1.0.9/plugins/dell/tests/efi/esrt/entries/entry2/lowest_supported_fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/esrt/entries/entry2/lowest_supported_fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi/fw_platform_size fwupd-1.2.10/plugins/dell/tests/efi/fw_platform_size --- fwupd-1.0.9/plugins/dell/tests/efi/fw_platform_size 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi/fw_platform_size 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +64 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/height fwupd-1.2.10/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/height --- fwupd-1.0.9/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/height 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/height 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +789 diff -Nru fwupd-1.0.9/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/width fwupd-1.2.10/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/width --- fwupd-1.0.9/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/width 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/width 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +456 diff -Nru fwupd-1.0.9/plugins/dell/tests/.gitignore fwupd-1.2.10/plugins/dell/tests/.gitignore --- fwupd-1.0.9/plugins/dell/tests/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/.gitignore 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +EFI +efi/efivars/fwupd-c34cb672-a81e-5d32-9d89-cbcabe8ec37b-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/dell/tests/test.bmp and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/dell/tests/test.bmp differ diff -Nru fwupd-1.0.9/plugins/dell/tests/tpm0/active fwupd-1.2.10/plugins/dell/tests/tpm0/active --- fwupd-1.0.9/plugins/dell/tests/tpm0/active 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/tpm0/active 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/tpm0/caps fwupd-1.2.10/plugins/dell/tests/tpm0/caps --- fwupd-1.0.9/plugins/dell/tests/tpm0/caps 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/tpm0/caps 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,3 @@ +Manufacturer: 0x49465800 +TCG version: 1.2 +Firmware version: 6.40 diff -Nru fwupd-1.0.9/plugins/dell/tests/tpm0/enabled fwupd-1.2.10/plugins/dell/tests/tpm0/enabled --- fwupd-1.0.9/plugins/dell/tests/tpm0/enabled 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/tpm0/enabled 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/tpm0/owned fwupd-1.2.10/plugins/dell/tests/tpm0/owned --- fwupd-1.0.9/plugins/dell/tests/tpm0/owned 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/tpm0/owned 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/dell/tests/tpm0/pcrs fwupd-1.2.10/plugins/dell/tests/tpm0/pcrs --- fwupd-1.0.9/plugins/dell/tests/tpm0/pcrs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell/tests/tpm0/pcrs 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/dell-dock/dell-dock.quirk fwupd-1.2.10/plugins/dell-dock/dell-dock.quirk --- fwupd-1.0.9/plugins/dell-dock/dell-dock.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/dell-dock.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,115 @@ +# +# Copyright (C) 2018 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 +# + +[DellDockUnlockTargets] +synapticsmst = 9 + +# Used to make plugin probe the devices +[DeviceInstanceId=USB\VID_413C&PID_B06F] +Name = Unprobed Dell accessory endpoint +Plugin = dell_dock +[DeviceInstanceId=USB\VID_413C&PID_B06E] +Name = Unprobed Dell accessory endpoint +Plugin = dell_dock + +# USB hub1 +[DeviceInstanceId=USB\VID_413C&PID_B06F&hub] +Name = RTS5413 in Dell dock +Summary = USB 3.1 Generation 1 Hub +ParentGuid = USB\VID_413C&PID_B06E&hub&embedded +Plugin = dell_dock +Vendor = Dell Inc +Icon = dock-usb +FirmwareSize = 0x10000 +Flags = require-ac,updatable +DellDockUnlockTarget = 8 +DellDockBlobMajorOffset = 0x7F6E +DellDockBlobMinorOffset = 0x7F6F +InstallDuration = 14 + +# USB hub2 +[DeviceInstanceId=USB\VID_413C&PID_B06E&hub] +Name = RTS5487 in Dell dock +Summary = USB 3.1 Generation 2 Hub +ParentGuid = USB\VID_413C&PID_B06E&hub&embedded +Vendor = Dell Inc +Plugin = dell_dock +Icon = dock-usb +FirmwareSize = 0x10000 +Flags = require-ac,updatable,has-bridge +DellDockUnlockTarget = 7 +DellDockBlobMajorOffset = 0x7F52 +DellDockBlobMinorOffset = 0x7F53 +InstallDuration = 3 + +# Embedded Controller +# Name is intentionally not set (it's queried by dock) +[Guid=USB\VID_413C&PID_B06E&hub&embedded] +Name = Dell dock +Summary = High performance dock +Plugin = dell_dock +Vendor = Dell Inc +Icon = dock-usb +FirmwareSizeMin = 0x1FFC0 +FirmwareSizeMax = 0x20000 +Flags = require-ac +Children = FuDellDockStatus|USB\VID_413C&PID_B06E&hub&status,FuDellDockMst|MST-panamera-vmm5331-259 +DellDockUnlockTarget = 1 +DellDockBoardMin = 6 +DellDockVersionLowest = 01.00.00.00 +DellDockBlobVersionOffset = 0x1AFC0 +InstallDuration = 60 + +# Representation of overall dock update +[DeviceInstanceId=USB\VID_413C&PID_B06E&hub&status] +Name = Package level of Dell dock +Summary = A representation of dock update status +Plugin = dell_dock +Vendor = Dell Inc +FirmwareSize = 24 +InstallDuration = 5 +DellDockBlobVersionOffset = 0x14 + +# MST Hub +[Guid=MST-panamera-vmm5331-259] +Name = VMM5331 in Dell dock +Summary = Multi Stream Transport controller +Vendor = Dell Inc +Plugin = synapticsmst +ParentGuid = USB\VID_413C&PID_B06E&hub&embedded +Flags = skip-restart,require-ac +FirmwareSize=524288 +DellDockUnlockTarget = 9 +InstallDuration = 95 +DellDockInstallDurationI2C=360 +DellDockBlobMajorOffset = 0x18400 +DellDockBlobMinorOffset = 0x18401 +DellDockBlobBuildOffset = 0x18402 + +# Thunderbolt controller +[Guid=TBT-00d4b070] +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 +InstallDuration = 22 +DellDockInstallDurationI2C = 181 +DellDockUnlockTarget = 10 +DellDockHubVersionLowest = 1.31 +DellDockBlobMajorOffset = 0x400a +DellDockBlobMinorOffset = 0x4009 diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-common.c fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-common.c --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 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 "fu-dell-dock-common.h" +#include "fu-device-locker.h" +#include "fu-dell-dock-i2c-ec.h" + +gboolean +fu_dell_dock_set_power (FuDevice *device, guint8 target, + gboolean enabled, GError **error) +{ + FuDevice *parent; + g_autoptr(FuDeviceLocker) locker = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + + parent = FU_IS_DELL_DOCK_EC (device) ? device : fu_device_get_parent (device); + + if (parent == NULL) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, + "Couldn't find parent for %s", + fu_device_get_name (device)); + return FALSE; + } + + locker = fu_device_locker_new (parent, error); + if (locker == NULL) + return FALSE; + + return fu_dell_dock_ec_modify_lock (parent, target, enabled, error); +} + +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 %" 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 -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-common.h fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-common.h --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 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 + */ + +#pragma once + +#include "config.h" + +#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_INSTANCE_ID "USB\\VID_413C&PID_B06E&hub&embedded" +#define DELL_DOCK_TBT_INSTANCE_ID "TBT-00d4b070" + +gboolean fu_dell_dock_set_power (FuDevice *device, + guint8 target, + gboolean enabled, + GError **error); +void fu_dell_dock_will_replug (FuDevice *device); + +void fu_dell_dock_clone_updatable (FuDevice *device); diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hid.c fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hid.c --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hid.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hid.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2018 Realtek Semiconductor Corporation + * Copyright (C) 2018 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 + +#include "fu-usb-device.h" +#include "fwupd-error.h" + +#include "fu-dell-dock-hid.h" + +#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 +#define HUB_CMD_WRITE_DATA 0x40 +#define HUB_EXT_READ_STATUS 0x09 +#define HUB_EXT_MCUMODIFYCLOCK 0x06 +#define HUB_EXT_I2C_WRITE 0xC6 +#define HUB_EXT_WRITEFLASH 0xC8 +#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; + guint8 ext; + union { + guint32 dwregaddr; + struct { + guint8 cmd_data0; + guint8 cmd_data1; + guint8 cmd_data2; + guint8 cmd_data3; + }; + }; + guint16 bufferlen; + FuHIDI2CParameters parameters; + guint8 extended_cmdarea[53]; + 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, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gboolean ret; + gsize actual_len = 0; + for (gint i = 1; i <= HID_MAX_RETRIES; i++) { + g_autoptr(GError) error_local = NULL; + ret = g_usb_device_control_transfer ( + usb_device, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, HID_REPORT_SET, 0x0200, + 0x0000, outbuffer, 192, &actual_len, + HIDI2C_TRANSACTION_TIMEOUT, NULL, &error_local); + if (ret) + break; + if (i == HID_MAX_RETRIES || + g_error_matches (error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE)) { + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } else { + g_debug ("attempt %d/%d: set control transfer failed: %s", + i, HID_MAX_RETRIES, + error_local->message); + sleep (1); + } + } + if (actual_len != 192) { + 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_dell_dock_hid_get_report (FuDevice *self, + guint8 *inbuffer, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gboolean ret; + gsize actual_len = 0; + for (gint i = 1; i <= HID_MAX_RETRIES; i++) { + g_autoptr(GError) error_local = NULL; + ret = g_usb_device_control_transfer ( + usb_device, G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, HID_REPORT_GET, 0x0100, + 0x0000, inbuffer, 192, &actual_len, + HIDI2C_TRANSACTION_TIMEOUT, NULL, &error_local); + if (ret) + break; + if (i == HID_MAX_RETRIES || + g_error_matches (error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE)) { + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } else { + g_debug ("attempt %d/%d: get control transfer failed: %s", + i, HID_MAX_RETRIES, + error_local->message); + } + } + if (actual_len != 192) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "only read %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_dell_dock_hid_get_hub_version (FuDevice *self, + GError **error) +{ + g_autofree gchar *version = NULL; + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_READ_DATA, + .ext = HUB_EXT_READ_STATUS, + .cmd_data0 = 0, + .cmd_data1 = 0, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = GUINT16_TO_LE (12), + .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .extended_cmdarea[0 ... 52] = 0, + }; + + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, + error)) { + g_prefix_error (error, "failed to query hub version: "); + return FALSE; + } + if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) { + g_prefix_error (error, "failed to query hub version: "); + return FALSE; + } + + version = g_strdup_printf ("%02x.%02x", + cmd_buffer.data[10], + cmd_buffer.data[11]); + fu_device_set_version (self, version, FWUPD_VERSION_FORMAT_PAIR); + return TRUE; +} + +gboolean +fu_dell_dock_hid_raise_mcu_clock (FuDevice *self, + gboolean enable, + GError **error) +{ + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_WRITE_DATA, + .ext = HUB_EXT_MCUMODIFYCLOCK, + .cmd_data0 = (guint8) enable, + .cmd_data1 = 0, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = 0, + .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .extended_cmdarea[0 ... 52] = 0, + }; + + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, + error)) { + g_prefix_error (error, + "failed to set mcu clock to %d: ", + enable); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_dell_dock_hid_get_ec_status (FuDevice *self, + guint8 *status1, + guint8 *status2, + GError **error) +{ + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_WRITE_DATA, + .ext = HUB_EXT_READ_STATUS, + .cmd_data0 = 0, + .cmd_data1 = 0, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = GUINT16_TO_LE (27), + .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .extended_cmdarea[0 ... 52] = 0, + }; + + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, + error)) { + g_prefix_error (error, "failed to get EC status: "); + return FALSE; + } + if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) { + g_prefix_error (error, "failed to get EC status: "); + return FALSE; + } + + *status1 = cmd_buffer.data[25]; + *status2 = cmd_buffer.data[26]; + + return TRUE; +} + +gboolean +fu_dell_dock_hid_erase_bank (FuDevice *self, guint8 idx, GError **error) +{ + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_WRITE_DATA, + .ext = HUB_EXT_ERASEBANK, + .cmd_data0 = 0, + .cmd_data1 = idx, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = 0, + .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .extended_cmdarea[0 ... 52] = 0, + }; + + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, + error)) { + g_prefix_error (error, "failed to erase bank: "); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_dell_dock_hid_write_flash (FuDevice *self, + guint32 dwAddr, + const guint8 *input, + gsize write_size, + GError **error) +{ + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_WRITE_DATA, + .ext = HUB_EXT_WRITEFLASH, + .dwregaddr = GUINT32_TO_LE (dwAddr), + .bufferlen = GUINT16_TO_LE (write_size), + .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .extended_cmdarea[0 ... 52] = 0, + }; + + g_return_val_if_fail (write_size <= HIDI2C_MAX_WRITE, FALSE); + + memcpy (cmd_buffer.data, input, write_size); + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, + error)) { + g_prefix_error ( + error, "failed to write %" G_GSIZE_FORMAT " flash to %x: ", + write_size, dwAddr); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_dell_dock_hid_verify_update (FuDevice *self, + gboolean *result, + GError **error) +{ + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_WRITE_DATA, + .ext = HUB_EXT_VERIFYUPDATE, + .cmd_data0 = 1, + .cmd_data1 = 0, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = GUINT16_TO_LE (1), + .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .extended_cmdarea[0 ... 52] = 0, + }; + + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, + error)) { + g_prefix_error (error, "failed to verify update: "); + return FALSE; + } + if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) { + g_prefix_error (error, "failed to verify update: "); + return FALSE; + } + *result = cmd_buffer.data[0]; + + return TRUE; +} + +gboolean +fu_dell_dock_hid_i2c_write (FuDevice *self, + const guint8 *input, + gsize write_size, + const FuHIDI2CParameters *parameters, + GError **error) +{ + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_WRITE_DATA, + .ext = HUB_EXT_I2C_WRITE, + .dwregaddr = 0, + .bufferlen = GUINT16_TO_LE (write_size), + .parameters = {.i2cslaveaddr = parameters->i2cslaveaddr, + .regaddrlen = 0, + .i2cspeed = parameters->i2cspeed | 0x80}, + .extended_cmdarea[0 ... 52] = 0, + }; + + g_return_val_if_fail (write_size <= HIDI2C_MAX_WRITE, FALSE); + + memcpy (cmd_buffer.data, input, write_size); + + return fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, error); +} + +gboolean +fu_dell_dock_hid_i2c_read (FuDevice *self, + guint32 cmd, + gsize read_size, + GBytes **bytes, + const FuHIDI2CParameters *parameters, + GError **error) +{ + FuHIDCmdBuffer cmd_buffer = { + .cmd = HUB_CMD_WRITE_DATA, + .ext = HUB_EXT_I2C_READ, + .dwregaddr = GUINT32_TO_LE (cmd), + .bufferlen = GUINT16_TO_LE (read_size), + .parameters = {.i2cslaveaddr = parameters->i2cslaveaddr, + .regaddrlen = parameters->regaddrlen, + .i2cspeed = parameters->i2cspeed | 0x80}, + .extended_cmdarea[0 ... 52] = 0, + .data[0 ... 191] = 0, + }; + + g_return_val_if_fail (read_size <= HIDI2C_MAX_READ, FALSE); + g_return_val_if_fail (bytes != NULL, FALSE); + g_return_val_if_fail (parameters->regaddrlen < HIDI2C_MAX_REGISTER, FALSE); + + if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, error)) + return FALSE; + if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) + return FALSE; + + *bytes = g_bytes_new (cmd_buffer.data, read_size); + + 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 -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hid.h fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hid.h --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hid.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hid.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 Realtek Semiconductor Corporation + * Copyright (C) 2018 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 + */ + +#pragma once + +#include "config.h" + +#include + +#include "fu-device.h" + +typedef struct __attribute__ ((packed)) { + guint8 i2cslaveaddr; + guint8 regaddrlen; + guint8 i2cspeed; +} FuHIDI2CParameters; + +typedef enum { + I2C_SPEED_250K, + I2C_SPEED_400K, + I2C_SPEED_800K, + /* */ + I2C_SPEED_LAST, +} BridgedI2CSpeed; + +#define HIDI2C_MAX_READ 192 +#define HIDI2C_MAX_WRITE 128 + +gboolean fu_dell_dock_hid_i2c_write (FuDevice *self, + const guint8 *input, + gsize write_size, + const FuHIDI2CParameters *parameters, + GError **error); +gboolean fu_dell_dock_hid_i2c_read (FuDevice *self, + guint32 cmd, + gsize read_size, + GBytes **bytes, + const FuHIDI2CParameters *parameters, + GError **error); + +gboolean fu_dell_dock_hid_get_hub_version (FuDevice *self, + GError **error); + +gboolean fu_dell_dock_hid_raise_mcu_clock (FuDevice *self, + gboolean enable, + GError **error); + +gboolean fu_dell_dock_hid_get_ec_status (FuDevice *self, + guint8 *status1, + guint8 *status2, + GError **error); + +gboolean fu_dell_dock_hid_erase_bank (FuDevice *self, + guint8 idx, + GError **error); + +gboolean fu_dell_dock_hid_write_flash (FuDevice *self, + guint32 addr, + const guint8 *input, + gsize write_size, + GError **error); + +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); diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hub.c fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hub.c --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hub.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hub.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2018 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 "fu-usb-device.h" +#include "fwupd-error.h" + +#include "fu-dell-dock-common.h" + +struct _FuDellDockHub { + FuUsbDevice parent_instance; + guint8 unlock_target; + guint64 blob_major_offset; + guint64 blob_minor_offset; +}; + +G_DEFINE_TYPE (FuDellDockHub, fu_dell_dock_hub, FU_TYPE_USB_DEVICE) + +static gboolean +fu_dell_dock_hub_probe (FuDevice *device, GError **error) +{ + g_autofree gchar *devid = NULL; + + devid = g_strdup_printf ("USB\\VID_%04X&PID_%04X&hub", + (guint) fu_usb_device_get_vid (FU_USB_DEVICE (device)), + (guint) fu_usb_device_get_pid (FU_USB_DEVICE (device))); + + fu_device_set_logical_id (device, "hub"); + fu_device_add_instance_id (device, devid); + + return TRUE; +} + +static gboolean +fu_dell_dock_hub_write_fw (FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockHub *self = FU_DELL_DOCK_HUB (device); + gsize fw_size = 0; + const guint8 *data = g_bytes_get_data (blob_fw, &fw_size); + gsize write_size = + (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; + gsize nwritten = 0; + guint32 address = 0; + gboolean result = FALSE; + g_autofree gchar *dynamic_version = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (blob_fw != NULL, FALSE); + + dynamic_version = g_strdup_printf ("%02x.%02x", + data[self->blob_major_offset], + data[self->blob_minor_offset]); + g_debug ("writing hub firmware version %s", dynamic_version); + + if (!fu_dell_dock_set_power (device, self->unlock_target, TRUE, error)) + return FALSE; + + if (!fu_dell_dock_hid_raise_mcu_clock (device, TRUE, error)) + return FALSE; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_dell_dock_hid_erase_bank (device, 1, error)) + return FALSE; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + do { + /* last packet */ + if (fw_size - nwritten < write_size) + write_size = fw_size - nwritten; + + if (!fu_dell_dock_hid_write_flash (device, address, data, + write_size, error)) + return FALSE; + nwritten += write_size; + data += write_size; + address += write_size; + fu_device_set_progress_full (device, nwritten, fw_size); + } while (nwritten < fw_size); + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY); + if (!fu_dell_dock_hid_verify_update (device, &result, error)) + return FALSE; + if (!result) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to verify the update"); + return FALSE; + } + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version (device, dynamic_version, FWUPD_VERSION_FORMAT_PAIR); + return TRUE; +} + +static gboolean +fu_dell_dock_hub_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDellDockHub *self = FU_DELL_DOCK_HUB (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; + } + if (g_strcmp0 (key, "DellDockBlobMajorOffset") == 0) { + self->blob_major_offset = fu_common_strtoull (value); + return TRUE; + } + 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_hub_open (FuUsbDevice *fu_usb_device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (fu_usb_device); + + /* open device and clear status */ + if (!g_usb_device_claim_interface ( + usb_device, 0, /* HID */ + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, error)) { + g_prefix_error (error, "failed to claim HID interface: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_dell_dock_hub_close (FuUsbDevice *fu_usb_device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (fu_usb_device); + + if (!g_usb_device_release_interface ( + usb_device, 0, /* HID */ + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, error)) { + g_prefix_error (error, "failed to release interface: "); + return FALSE; + } + + return TRUE; +} + +static void +fu_dell_dock_hub_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_dell_dock_hub_parent_class)->finalize (object); +} + +static void +fu_dell_dock_hub_init (FuDellDockHub *self) +{ +} + +static void +fu_dell_dock_hub_class_init (FuDellDockHubClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + object_class->finalize = fu_dell_dock_hub_finalize; + klass_usb_device->open = fu_dell_dock_hub_open; + klass_usb_device->close = fu_dell_dock_hub_close; + klass_device->setup = fu_dell_dock_hid_get_hub_version; + klass_device->probe = fu_dell_dock_hub_probe; + klass_device->write_firmware = fu_dell_dock_hub_write_fw; + klass_device->set_quirk_kv = fu_dell_dock_hub_set_quirk_kv; +} + +FuDellDockHub * +fu_dell_dock_hub_new (FuUsbDevice *device) +{ + FuDellDockHub *self = g_object_new (FU_TYPE_DELL_DOCK_HUB, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hub.h fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hub.h --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-hub.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-hub.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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 + */ + +#pragma once + +#include "config.h" + +#include "fu-usb-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_DELL_DOCK_HUB (fu_dell_dock_hub_get_type ()) +G_DECLARE_FINAL_TYPE (FuDellDockHub, fu_dell_dock_hub, FU, DELL_DOCK_HUB, FuUsbDevice) + +FuDellDockHub *fu_dell_dock_hub_new (FuUsbDevice *device); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-ec.c fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-ec.c --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-ec.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-ec.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,1027 @@ +/* + * Copyright (C) 2018 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 "fu-common-version.h" +#include "fu-usb-device.h" +#include "fwupd-error.h" + +#include "fu-dell-dock-common.h" + +#define I2C_EC_ADDRESS 0xec + +#define EC_CMD_SET_DOCK_PKG 0x01 +#define EC_CMD_GET_DOCK_INFO 0x02 +#define EC_CMD_GET_DOCK_DATA 0x03 +#define EC_CMD_GET_DOCK_TYPE 0x05 +#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->board_id); + summary = fu_device_get_metadata (device, board_type_str); + if (summary != NULL) + fu_device_set_summary (device, summary); +} + +FuDevice * +fu_dell_dock_ec_get_symbiote (FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + + return self->symbiote; +} + +gboolean +fu_dell_dock_ec_needs_tbt (FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + gboolean port0_tbt_mode = self->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); + + if (self->passive_flow > 0) { + self->passive_flow |= PASSIVE_TBT_MASK; + return TRUE; + } + return FALSE; +} + +static const gchar* +fu_dell_dock_devicetype_to_str (guint device_type, guint sub_type) +{ + switch (device_type) { + case FU_DELL_DOCK_DEVICETYPE_MAIN_EC: + return "EC"; + case FU_DELL_DOCK_DEVICETYPE_MST: + return "MST"; + case FU_DELL_DOCK_DEVICETYPE_TBT: + return "Thunderbolt"; + case FU_DELL_DOCK_DEVICETYPE_HUB: + if (sub_type == SUBTYPE_GEN2) + return "USB 3.1 Gen2"; + else if (sub_type == SUBTYPE_GEN1) + return "USB 3.1 Gen1"; + return NULL; + case FU_DELL_DOCK_DEVICETYPE_PD: + return "PD"; + default: + return NULL; + } +} + +static gboolean +fu_dell_dock_ec_read (FuDevice *device, guint32 cmd, gsize length, + GBytes **bytes, GError **error) +{ + /* The first byte of result data will be the size of the return, + hide this from callers */ + guint8 result_length = length + 1; + g_autoptr(GBytes) bytes_local = NULL; + const guint8 *result; + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (self->symbiote != NULL, FALSE); + g_return_val_if_fail (bytes != NULL, FALSE); + + if (!fu_dell_dock_hid_i2c_read (self->symbiote, cmd, result_length, + &bytes_local, &ec_base_settings, error)) { + g_prefix_error (error, "read over HID-I2C failed: "); + return FALSE; + } + result = g_bytes_get_data (bytes_local, NULL); + /* first byte of result should be size of our data */ + if (result[0] != length) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Invalid result data: %d expected %" G_GSIZE_FORMAT, + result[0], length); + return FALSE; + } + *bytes = g_bytes_new (result + 1, length); + + return TRUE; +} + +static gboolean +fu_dell_dock_ec_write (FuDevice *device, gsize length, guint8 *data, GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (self->symbiote != NULL, FALSE); + g_return_val_if_fail (length > 1, FALSE); + + if (!fu_dell_dock_hid_i2c_write (self->symbiote, data, length, + &ec_base_settings, error)) { + g_prefix_error (error, "write over HID-I2C failed: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_dell_dock_is_valid_dock (FuDevice *device, GError **error) +{ + g_autoptr(GBytes) data = NULL; + const guint8 *result = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + + if (!fu_dell_dock_ec_read (device, EC_CMD_GET_DOCK_TYPE, 1, &data, error)) { + g_prefix_error (error, "Failed to query dock type: "); + return FALSE; + } + result = g_bytes_get_data (data, NULL); + + if (result == NULL || *result != EXPECTED_DOCK_TYPE) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, + "No valid dock was found"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_dell_dock_ec_get_dock_info (FuDevice *device, + GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + const FuDellDockDockInfoHeader *header = NULL; + const FuDellDockEcQueryEntry *device_entry = NULL; + const FuDellDockEcAddrMap *map = NULL; + const gchar *hub_version; + guint32 oldest_base_pd = 0; + g_autoptr(GBytes) data = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + + if (!fu_dell_dock_ec_read (device, EC_CMD_GET_DOCK_INFO, + EXPECTED_DOCK_INFO_SIZE, + &data, error)) { + g_prefix_error (error, "Failed to query dock info: "); + return FALSE; + } + if (!g_bytes_get_data (data, NULL)) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, + "Failed to read dock info"); + return FALSE; + } + + header = (FuDellDockDockInfoHeader *) g_bytes_get_data (data, NULL); + if (!header) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, + "Failed to parse dock info"); + return FALSE; + } + + /* guard against EC not yet ready and fail init */ + if (header->total_devices == 0) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID, + "No bridge devices detected, dock may be booting up"); + return FALSE; + } + g_debug ("%u devices [%u->%u]", + header->total_devices, + header->first_index, + header->last_index); + device_entry = + (FuDellDockEcQueryEntry *) ((guint8 *) header + sizeof(FuDellDockDockInfoHeader)); + for (guint i = 0; i < header->total_devices; i++) { + const gchar *type_str; + map = &(device_entry[i].ec_addr_map); + type_str = fu_dell_dock_devicetype_to_str (map->device_type, map->sub_type); + if (type_str == NULL) + continue; + g_debug ("#%u: %s in %s (A: %u I: %u)", i, type_str, + (map->location == LOCATION_BASE) ? "Base" : "Module", + map->arg, map->instance); + g_debug ("\tVersion32: %08x\tVersion8: %x %x %x %x", + device_entry[i].version.version_32, + device_entry[i].version.version_8[0], + device_entry[i].version.version_8[1], + device_entry[i].version.version_8[2], + device_entry[i].version.version_8[3]); + /* BCD but guint32 */ + if (map->device_type == FU_DELL_DOCK_DEVICETYPE_MAIN_EC) { + self->raw_versions->ec_version = device_entry[i].version.version_32; + self->ec_version = g_strdup_printf ( + "%02x.%02x.%02x.%02x", device_entry[i].version.version_8[0], + device_entry[i].version.version_8[1], + 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 (FU_DEVICE (self), self->ec_version, FWUPD_VERSION_FORMAT_QUAD); + + } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_MST) { + self->raw_versions->mst_version = device_entry[i].version.version_32; + /* guard against invalid MST version read from EC */ + if (!fu_dell_dock_test_valid_byte (device_entry[i].version.version_8, 1)) { + g_warning ("[EC Bug] EC read invalid MST version %08x", + device_entry[i].version.version_32); + continue; + } + self->mst_version = g_strdup_printf ("%02x.%02x.%02x", + device_entry[i].version.version_8[1], + 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 && + 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", + device_entry[i].version.version_32); + continue; + } + self->raw_versions->tbt_version = device_entry[i].version.version_32; + self->tbt_version = g_strdup_printf ("%02x.%02x", + device_entry[i].version.version_8[2], + device_entry[i].version.version_8[3]); + g_debug ("\tParsed version %s", self->tbt_version); + } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_HUB) { + g_debug ("\thub subtype: %u", map->sub_type); + if (map->sub_type == SUBTYPE_GEN2) + self->raw_versions->hub2_version = device_entry[i].version.version_32; + else if (map->sub_type == SUBTYPE_GEN1) + self->raw_versions->hub1_version = device_entry[i].version.version_32; + } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_PD && + map->location == LOCATION_BASE && + map->sub_type == 0) { + if (oldest_base_pd == 0 || + device_entry[i].version.version_32 < oldest_base_pd) + oldest_base_pd = GUINT32_TO_BE (device_entry[i].version.version_32); + g_debug ("\tParsed version: %02x.%02x.%02x.%02x", + device_entry[i].version.version_8[0], + device_entry[i].version.version_8[1], + device_entry[i].version.version_8[2], + device_entry[i].version.version_8[3]); + } + } + + /* 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 (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; + } + + fu_device_set_version_lowest (device, self->ec_minimum_version); + + + /* Determine if the passive flow should be used when flashing */ + hub_version = fu_device_get_version (self->symbiote); + if (fu_common_vercmp (hub_version, "1.42") >= 0) { + g_debug ("using passive flow"); + self->passive_flow = PASSIVE_REBOOT_MASK; + fu_device_set_custom_flags (device, "skip-restart"); + } else { + g_debug ("not using passive flow (EC: %s Hub2: %s)", + self->ec_version, hub_version); + } + return TRUE; +} + +static gboolean +fu_dell_dock_ec_get_dock_data (FuDevice *device, + GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + g_autoptr(GBytes) data = NULL; + g_autoptr(GString) name = NULL; + gchar service_tag[8] = {0x00}; + const guint8 *result; + gsize length = sizeof(FuDellDockDockDataStructure); + g_autofree gchar *bundled_serial = NULL; + FuDellDockECFWUpdateStatus status; + + g_return_val_if_fail (device != NULL, FALSE); + + if (!fu_dell_dock_ec_read (device, EC_CMD_GET_DOCK_DATA, length, + &data, error)) { + g_prefix_error (error, "Failed to query dock info: "); + return FALSE; + } + result = g_bytes_get_data (data, NULL); + if (result == NULL) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, + "Failed to read dock data"); + return FALSE; + } + if (g_bytes_get_size (data) != length) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, + "Unexpected dock data size %" G_GSIZE_FORMAT, + g_bytes_get_size (data)); + return FALSE; + } + memcpy (self->data, result, length); + + /* guard against EC not yet ready and fail init */ + name = g_string_new (self->data->marketing_name); + if (name->len > 0) + fu_device_set_name (device, name->str); + else + g_warning ("[EC bug] Invalid dock name detected"); + + if (self->data->module_type >= 0xfe) + g_warning ("[EC bug] Invalid module type 0x%02x", + self->data->module_type); + + /* set serial number */ + memcpy (service_tag, self->data->service_tag, 7); + bundled_serial = g_strdup_printf ("%s/%08" G_GUINT64_FORMAT, + service_tag, + self->data->module_serial); + fu_device_set_serial (device, bundled_serial); + + /* copy this for being able to send in next commit transaction */ + self->raw_versions->pkg_version = self->data->dock_firmware_pkg_ver; + + /* read if passive update pending */ + if (!fu_dell_dock_get_ec_status (device, &status, error)) + return FALSE; + + /* make sure this hardware spin matches our expecations */ + if (self->data->board_id >= self->board_min) { + 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_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + 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)); + } + + return TRUE; +} + +static void +fu_dell_dock_ec_to_string (FuDevice *device, GString *str) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + gchar service_tag[8] = {0x00}; + + g_string_append (str, " FuDellDellDockEc:\n"); + g_string_append_printf (str, "\tboard ID: %u\n", + self->data->board_id); + g_string_append_printf (str, "\tpower supply: %uW\n", + self->data->power_supply_wattage); + g_string_append_printf (str, "\tstatus (port0): %x\n", + self->data->port0_dock_status); + g_string_append_printf (str, "\tstatus (port1): %x\n", + self->data->port1_dock_status); + memcpy (service_tag, self->data->service_tag, 7); + g_string_append_printf (str, "\tservice tag: %s\n", + service_tag); + g_string_append_printf (str, "\tconfiguration: %u\n", + self->data->dock_configuration); + g_string_append_printf (str, "\tpackage firmware version: %x\n", + self->data->dock_firmware_pkg_ver); + g_string_append_printf (str, "\tmodule serial #: %08" G_GUINT64_FORMAT "\n", + self->data->module_serial); + g_string_append_printf (str, "\toriginal module serial #: %08" G_GUINT64_FORMAT "\n", + self->data->original_module_serial); + g_string_append_printf (str, "\ttype: %u\n", + self->data->dock_type); + g_string_append_printf (str, "\tmodule type: %x\n", + self->data->module_type); + g_string_append_printf (str, "\tminimum ec: %s\n", + self->ec_minimum_version); + g_string_append_printf (str, "\tpassive flow: %d\n", + self->passive_flow); +} + +gboolean +fu_dell_dock_ec_modify_lock (FuDevice *device, + guint8 target, + gboolean unlocked, + GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + guint32 cmd; + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (target != 0, FALSE); + + cmd = EC_CMD_MODIFY_LOCK | /* cmd */ + 2 << 8 | /* length of data arguments */ + target << 16 | /* device to operate on */ + unlocked << 24; /* unlock/lock */ + + + if (!fu_dell_dock_ec_write (device, 4, (guint8 *) &cmd, error)) { + g_prefix_error (error, "Failed to unlock device %d: ", target); + return FALSE; + } + g_debug ("Modified lock for %d to %d through %s (%s)", + target, unlocked, + 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; +} + +static gboolean +fu_dell_dock_ec_reset (FuDevice *device, GError **error) +{ + guint16 cmd = EC_CMD_RESET; + + g_return_val_if_fail (device != NULL, FALSE); + + return fu_dell_dock_ec_write (device, 2, (guint8 *) &cmd, error); +} + +static gboolean +fu_dell_dock_ec_activate (FuDevice *device, GError **error) +{ + FuDellDockECFWUpdateStatus status; + + /* read if passive update pending */ + if (!fu_dell_dock_get_ec_status (device, &status, error)) + return FALSE; + + if (status != FW_UPDATE_IN_PROGRESS) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "No firmware update pending for %s", + fu_device_get_name (device)); + return FALSE; + } + + return fu_dell_dock_ec_reset (device, error); +} + +gboolean +fu_dell_dock_ec_reboot_dock (FuDevice *device, GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + + g_return_val_if_fail (device != NULL, FALSE); + + 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); + } + + return TRUE; +} + +static gboolean +fu_dell_dock_get_ec_status (FuDevice *device, + FuDellDockECFWUpdateStatus *status_out, + GError **error) +{ + g_autoptr(GBytes) data = NULL; + const guint8 *result = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (status_out != NULL, FALSE); + + if (!fu_dell_dock_ec_read (device, EC_GET_FW_UPDATE_STATUS, 1, + &data, error)) { + g_prefix_error (error, "Failed to read FW update status: "); + return FALSE; + } + result = g_bytes_get_data (data, NULL); + + if (!result) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, + "Failed to read FW update status"); + return FALSE; + } + *status_out = *result; + return TRUE; +} + +const gchar* +fu_dell_dock_ec_get_tbt_version (FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + return self->tbt_version; +} + +const gchar* +fu_dell_dock_ec_get_mst_version (FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + return self->mst_version; +} + +guint32 +fu_dell_dock_ec_get_status_version (FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + return self->raw_versions->pkg_version; +} + +gboolean +fu_dell_dock_ec_commit_package (FuDevice *device, GBytes *blob_fw, + GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + gsize length = 0; + const guint8 *data = g_bytes_get_data (blob_fw, &length); + g_autofree guint8 *payload = g_malloc0 (length + 2); + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (blob_fw != NULL, FALSE); + + if (length != sizeof(FuDellDockDockPackageFWVersion)) { + g_set_error (error, G_IO_ERR, G_IO_ERROR_INVALID_DATA, + "Invalid package size %" G_GSIZE_FORMAT, + length); + return FALSE; + } + memcpy (self->raw_versions, data, length); + + g_debug ("Committing (%zu) bytes ", sizeof(FuDellDockDockPackageFWVersion)); + g_debug ("\tec_version: %x", self->raw_versions->ec_version); + g_debug ("\tmst_version: %x", self->raw_versions->mst_version); + g_debug ("\thub1_version: %x", self->raw_versions->hub1_version); + g_debug ("\thub2_version: %x", self->raw_versions->hub2_version); + g_debug ("\ttbt_version: %x", self->raw_versions->tbt_version); + g_debug ("\tpkg_version: %x", self->raw_versions->pkg_version); + + payload [0] = EC_CMD_SET_DOCK_PKG; + payload [1] = length; + memcpy (payload + 2, data, length); + + if (!fu_dell_dock_ec_write (device, length + 2, payload, error)) { + g_prefix_error (error, "Failed to query dock info: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_dell_dock_ec_write_fw (FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + FuDellDockECFWUpdateStatus status = FW_UPDATE_IN_PROGRESS; + guint8 progress1 = 0, progress0 = 0; + gsize fw_size = 0; + const guint8 *data = g_bytes_get_data (blob_fw, &fw_size); + gsize write_size = + (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; + gsize nwritten = 0; + guint32 address = 0 | 0xff << 24; + g_autofree gchar *dynamic_version = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (blob_fw != NULL, FALSE); + + dynamic_version = g_strndup ((gchar *) data + self->blob_version_offset, 11); + g_debug ("writing EC firmware version %s", dynamic_version); + + if (!fu_dell_dock_ec_modify_lock (device, self->unlock_target, TRUE, error)) + return FALSE; + + if (!fu_dell_dock_hid_raise_mcu_clock (self->symbiote, TRUE, error)) + return FALSE; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_dell_dock_hid_erase_bank (self->symbiote, 0xff, error)) + return FALSE; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + do { + /* last packet */ + if (fw_size - nwritten < write_size) + write_size = fw_size - nwritten; + + if (!fu_dell_dock_hid_write_flash (self->symbiote, address, data, + write_size, error)) { + g_prefix_error (error, "write over HID failed: "); + return FALSE; + } + fu_device_set_progress_full (device, nwritten, fw_size); + nwritten += write_size; + data += write_size; + address += write_size; + } while (nwritten < fw_size); + + 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, FWUPD_VERSION_FORMAT_QUAD); + + /* 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"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + return TRUE; + } + + if (!fu_dell_dock_ec_reset (device, error)) + return FALSE; + + /* notify daemon that this device will need to replug */ + fu_dell_dock_will_replug (device); + + /* poll for completion status */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY); + while (status != FW_UPDATE_COMPLETE) { + g_autoptr(GError) error_local = NULL; + + if (!fu_dell_dock_hid_get_ec_status (self->symbiote, &progress1, + &progress0, error)) { + g_prefix_error (error, "Failed to read scratch: "); + return FALSE; + } + g_debug ("Read %u and %u from scratch", progress1, progress0); + if (progress0 > 100) + progress0 = 100; + fu_device_set_progress_full (device, progress0, 100); + + /* This is expected to fail until update is done */ + if (!fu_dell_dock_get_ec_status (device, &status, + &error_local)) { + g_debug ("Flash EC Received result: %s (status %u)", + 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; + } + } + + return TRUE; +} + +static gboolean +fu_dell_dock_ec_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (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; + } + if (g_strcmp0 (key, "DellDockBoardMin") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp < G_MAXUINT8) { + self->board_min = tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid DellDockBoardMin"); + return FALSE; + } + if (g_strcmp0 (key, "DellDockVersionLowest") == 0) { + self->ec_minimum_version = g_strdup (value); + return TRUE; + } + if (g_str_has_prefix (key, "DellDockBoard")) { + fu_device_set_metadata (device, key, value); + return TRUE; + } + if (g_strcmp0 (key, "DellDockBlobVersionOffset") == 0) { + self->blob_version_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_ec_probe (FuDevice *device, GError **error) +{ + /* this will trigger setting up all the quirks */ + fu_device_add_instance_id (device, DELL_DOCK_EC_INSTANCE_ID); + + return TRUE; +} + +static gboolean +fu_dell_dock_ec_query (FuDevice *device, GError **error) +{ + if (!fu_dell_dock_ec_get_dock_data (device, error)) + return FALSE; + + return fu_dell_dock_ec_get_dock_info (device, error); +} + +static gboolean +fu_dell_dock_ec_setup (FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + GPtrArray *children; + + /* if query looks bad, wait a few seconds and retry */ + if (!fu_dell_dock_ec_query (device, &error_local)) { + if (g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID)) { + g_warning ("%s", error_local->message); + g_usleep (2 * G_USEC_PER_SEC); + if (!fu_dell_dock_ec_query (device, error)) + return FALSE; + } else { + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + } + + /* call setup on all the children we produced */ + children = fu_device_get_children (device); + for (guint i=0 ; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + g_autoptr(FuDeviceLocker) locker = NULL; + g_debug ("setup %s", + fu_device_get_name (child)); + locker = fu_device_locker_new (child, error); + if (locker == NULL) + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_dell_dock_ec_open (FuDevice *device, GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + + if (!fu_device_open (self->symbiote, error)) + return FALSE; + + return fu_dell_dock_is_valid_dock (device, error); +} + +static gboolean +fu_dell_dock_ec_close (FuDevice *device, GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (device); + + return fu_device_close (self->symbiote, error); +} + +static void +fu_dell_dock_ec_finalize (GObject *object) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC (object); + g_object_unref (self->symbiote); + g_free (self->ec_version); + g_free (self->mst_version); + g_free (self->tbt_version); + g_free (self->data); + g_free (self->raw_versions); + g_free (self->ec_minimum_version); + G_OBJECT_CLASS (fu_dell_dock_ec_parent_class)->finalize (object); +} + +static void +fu_dell_dock_ec_init (FuDellDockEc *self) +{ + self->data = g_new0 (FuDellDockDockDataStructure, 1); + self->raw_versions = g_new0 (FuDellDockDockPackageFWVersion, 1); +} + +static void +fu_dell_dock_ec_class_init (FuDellDockEcClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + object_class->finalize = fu_dell_dock_ec_finalize; + klass_device->activate = fu_dell_dock_ec_activate; + klass_device->to_string = fu_dell_dock_ec_to_string; + klass_device->probe = fu_dell_dock_ec_probe; + klass_device->setup = fu_dell_dock_ec_setup; + klass_device->open = fu_dell_dock_ec_open; + klass_device->close = fu_dell_dock_ec_close; + klass_device->write_firmware = fu_dell_dock_ec_write_fw; + klass_device->set_quirk_kv = fu_dell_dock_ec_set_quirk_kv; +} + +FuDellDockEc * +fu_dell_dock_ec_new (FuDevice *symbiote) +{ + FuDellDockEc *self = NULL; + + self = g_object_new (FU_TYPE_DELL_DOCK_EC, NULL); + self->symbiote = g_object_ref (symbiote); + fu_device_set_physical_id (FU_DEVICE (self), + fu_device_get_physical_id (self->symbiote)); + fu_device_set_logical_id (FU_DEVICE (self), "ec"); + + return self; +} diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-ec.h fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-ec.h --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-ec.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-ec.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 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 + */ + +#pragma once + +#include "config.h" + +#include + +#include "fu-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_DELL_DOCK_EC (fu_dell_dock_ec_get_type ()) +G_DECLARE_FINAL_TYPE (FuDellDockEc, fu_dell_dock_ec, FU, DELL_DOCK_EC, FuDevice) + +FuDellDockEc *fu_dell_dock_ec_new (FuDevice *symbiote); + +G_END_DECLS + +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, + GError **error); + +gboolean fu_dell_dock_ec_reboot_dock (FuDevice *device, + GError **error); + +const gchar *fu_dell_dock_ec_get_mst_version (FuDevice *device); +const gchar *fu_dell_dock_ec_get_tbt_version (FuDevice *device); +guint32 fu_dell_dock_ec_get_status_version (FuDevice *device); +gboolean fu_dell_dock_ec_commit_package (FuDevice *device, + GBytes *blob_fw, + GError **error); +FuDevice *fu_dell_dock_ec_get_symbiote (FuDevice *device); diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-mst.c fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-mst.c --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-mst.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-mst.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,990 @@ +/* + * Copyright (C) 2018 Synaptics + * Copyright (C) 2018 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 "fu-common.h" + +#include "fu-dell-dock-common.h" + +#define I2C_MST_ADDRESS 0x72 + +/* MST registers */ +#define MST_RC_TRIGGER_ADDR 0x2000fc +#define MST_CORE_MCU_BOOTLOADER_STS 0x20010c +#define MST_RC_COMMAND_ADDR 0x200110 +#define MST_RC_OFFSET_ADDR 0x200114 +#define MST_RC_LENGTH_ADDR 0x200118 +#define MST_RC_DATA_ADDR 0x200120 +#define MST_CORE_MCU_FW_VERSION 0x200160 +#define MST_REG_QUAD_DISABLE 0x200fc0 +#define MST_REG_HDCP22_DISABLE 0x200f90 + +/* MST remote control commands */ +#define MST_CMD_ENABLE_REMOTE_CONTROL 0x1 +#define MST_CMD_DISABLE_REMOTE_CONTROL 0x2 +#define MST_CMD_CHECKSUM 0x11 +#define MST_CMD_ERASE_FLASH 0x14 +#define MST_CMD_WRITE_FLASH 0x20 +#define MST_CMD_READ_FLASH 0x30 +#define MST_CMD_WRITE_MEMORY 0x21 +#define MST_CMD_READ_MEMORY 0x31 + +/* Arguments related to flashing */ +#define FLASH_SECTOR_ERASE_4K 0x1000 +#define FLASH_SECTOR_ERASE_32K 0x2000 +#define FLASH_SECTOR_ERASE_64K 0x3000 +#define EEPROM_TAG_OFFSET 0x1fff0 +#define EEPROM_BANK_OFFSET 0x20000 +#define EEPROM_ESM_OFFSET 0x40000 + +/* Flash offsets */ +#define MST_BOARDID_OFFSET 0x10e + +/* Remote control offsets */ +#define MST_CHIPID_OFFSET 0x1500 + +/* magic triggers */ +#define MST_TRIGGER_WRITE 0xf2 +#define MST_TRIGGER_REBOOT 0xf5 + +/* IDs used in DELL_DOCK */ +#define EXPECTED_CHIPID 0x5331 + +/* firmware file offsets */ +#define MST_BLOB_VERSION_OFFSET 0x06F0 + +typedef enum { + Bank0, + Bank1, + ESM, +} MSTBank; + +typedef struct { + guint start; + guint length; +} MSTBankAttributes; + +const MSTBankAttributes bank0_attributes = { + .start = 0, + .length = EEPROM_BANK_OFFSET, +}; + +const MSTBankAttributes bank1_attributes = { + .start = EEPROM_BANK_OFFSET, + .length = EEPROM_BANK_OFFSET, +}; + +const MSTBankAttributes esm_attributes = { + .start = EEPROM_ESM_OFFSET, + .length = 0x3ffff +}; + +FuHIDI2CParameters mst_base_settings = { + .i2cslaveaddr = I2C_MST_ADDRESS, + .regaddrlen = 0, + .i2cspeed = I2C_SPEED_400K, +}; + +struct _FuDellDockMst { + FuDevice parent_instance; + FuDevice *symbiote; + guint8 unlock_target; + guint64 blob_major_offset; + guint64 blob_minor_offset; + guint64 blob_build_offset; +}; + +G_DEFINE_TYPE (FuDellDockMst, fu_dell_dock_mst, FU_TYPE_DEVICE) + +/** + * fu_dell_dock_mst_get_bank_attribs: + * @bank: An MSTBank + * @out (out): The MSTBankAttributes attribute that matches + * @error: the #GError, or %NULL + * + * Returns a structure that corresponds to the attributes for a bank + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dell_dock_mst_get_bank_attribs (MSTBank bank, + const MSTBankAttributes **out, + GError **error) +{ + switch (bank) { + case Bank0: + *out = &bank0_attributes; + break; + case Bank1: + *out = &bank1_attributes; + break; + case ESM: + *out = &esm_attributes; + break; + default: + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Invalid bank specified %u", bank); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_dell_dock_mst_rc_command (FuDevice *symbiote, + guint8 cmd, + guint32 length, + guint32 offset, + const guint8 *data, + GError **error); + +static gboolean +fu_dell_dock_mst_read_register (FuDevice *symbiote, + guint32 address, + gsize length, + GBytes **bytes, + GError **error) +{ + g_return_val_if_fail (symbiote != NULL, FALSE); + g_return_val_if_fail (bytes != NULL, FALSE); + g_return_val_if_fail (length <= 32, FALSE); + + /* write the offset we're querying */ + if (!fu_dell_dock_hid_i2c_write (symbiote, (guint8 *) &address, 4, + &mst_base_settings, error)) + return FALSE; + + /* read data for the result */ + if (!fu_dell_dock_hid_i2c_read (symbiote, 0, length, bytes, + &mst_base_settings, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_write_register (FuDevice *symbiote, + guint32 address, + guint8 *data, + gsize length, + GError **error) +{ + g_autofree guint8 *buffer = g_malloc0 (length + 4); + memcpy (buffer, &address, 4); + memcpy (buffer + 4, data, length); + + g_return_val_if_fail (symbiote != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + /* write the offset we're querying */ + return fu_dell_dock_hid_i2c_write (symbiote, buffer, length + 4, + &mst_base_settings, error); +} + +static gboolean +fu_dell_dock_mst_query_active_bank (FuDevice *symbiote, + MSTBank *active, + GError **error) +{ + g_autoptr(GBytes) bytes = NULL; + const guint32 *data = NULL; + gsize length = 4; + + if (!fu_dell_dock_mst_read_register (symbiote, MST_CORE_MCU_BOOTLOADER_STS, + length, &bytes, error)) { + g_prefix_error (error, "Failed to query active bank: "); + return FALSE; + } + + data = g_bytes_get_data (bytes, &length); + if ((data[0] & (1 << 7)) || (data[0] & (1 << 30))) + *active = Bank1; + else + *active = Bank0; + g_debug ("MST: active bank is: %u", *active); + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_disable_remote_control (FuDevice *symbiote, GError **error) +{ + g_debug ("MST: Disabling remote control"); + return fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_DISABLE_REMOTE_CONTROL, + 0, 0, + NULL, + error); +} + +static gboolean +fu_dell_dock_mst_enable_remote_control (FuDevice *symbiote, GError **error) +{ + g_autoptr(GError) error_local = NULL; + const gchar *data = "PRIUS"; + + g_debug ("MST: Enabling remote control"); + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_ENABLE_REMOTE_CONTROL, + 5, 0, + (guint8 *) data, + &error_local)) { + g_debug ("Failed to enable remote control: %s", + error_local->message); + /* try to disable / re-enable */ + if (!fu_dell_dock_mst_disable_remote_control (symbiote, error)) + return FALSE; + return fu_dell_dock_mst_enable_remote_control (symbiote, error); + } + return TRUE; +} + +static gboolean +fu_dell_dock_trigger_rc_command (FuDevice *symbiote, GError **error) +{ + const guint8 *result = NULL; + guint32 tmp; + + /* Trigger the write */ + tmp = MST_TRIGGER_WRITE; + if (!fu_dell_dock_mst_write_register (symbiote, + MST_RC_TRIGGER_ADDR, + (guint8 *) &tmp, sizeof(guint32), + error)) { + g_prefix_error (error, "Failed to write MST_RC_TRIGGER_ADDR: "); + return FALSE; + } + /* poll for completion */ + tmp = 0xffff; + for (guint i = 0; i < 1000; i++) { + g_autoptr(GBytes) bytes = NULL; + if (!fu_dell_dock_mst_read_register (symbiote, + MST_RC_COMMAND_ADDR, + sizeof(guint32), &bytes, + error)) { + g_prefix_error (error, + "Failed to poll MST_RC_COMMAND_ADDR"); + return FALSE; + } + result = g_bytes_get_data (bytes, NULL); + /* complete */ + if ((result[2] & 0x80) == 0) { + tmp = result[3]; + break; + } + g_usleep (2000); + } + switch (tmp) { + /* need to enable remote control */ + case 4: + return fu_dell_dock_mst_enable_remote_control (symbiote, error); + /* error scenarios */ + case 3: + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unknown error"); + return FALSE; + case 2: + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unsupported command"); + return FALSE; + case 1: + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid argument"); + return FALSE; + /* success scenario */ + case 0: + return TRUE; + + default: + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Command timed out or unknown failure: %x", + tmp); + return FALSE; + } +} + +static gboolean +fu_dell_dock_mst_rc_command (FuDevice *symbiote, + guint8 cmd, + guint32 length, + guint32 offset, + const guint8 *data, + GError **error) +{ + /* 4 for cmd, 4 for offset, 4 for length, 4 for garbage */ + gint buffer_len = (data == NULL) ? 12 : length + 16; + g_autofree guint8 *buffer = g_malloc0 (buffer_len); + guint32 tmp; + + g_return_val_if_fail (symbiote != NULL, FALSE); + + /* command */ + tmp = (cmd | 0x80) << 16; + memcpy (buffer, &tmp, 4); + /* offset */ + memcpy (buffer + 4, &offset, 4); + /* length */ + memcpy (buffer + 8, &length, 4); + /* data */ + if (data != NULL) + memcpy (buffer + 16, data, length); + + /* write the combined register stream */ + if (!fu_dell_dock_mst_write_register (symbiote, MST_RC_COMMAND_ADDR, + buffer, buffer_len, error)) + return FALSE; + + return fu_dell_dock_trigger_rc_command (symbiote, error); +} + +static gboolean +fu_dell_dock_mst_read_chipid (FuDevice *symbiote, + guint16 *chip_id, + GError **error) +{ + g_autoptr(GBytes) bytes = NULL; + const guint8 *data; + gsize length = 4; + + g_return_val_if_fail (chip_id != NULL, FALSE); + + /* run an RC command to get data from memory */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_READ_MEMORY, + length, MST_CHIPID_OFFSET, + NULL, + error)) + return FALSE; + if (!fu_dell_dock_mst_read_register (symbiote, + MST_RC_DATA_ADDR, + length, + &bytes, + error)) + return FALSE; + data = g_bytes_get_data (bytes, &length); + *chip_id = (data[1] << 8) | data[2]; + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_check_offset (guint8 byte, guint8 offset) +{ + if ((byte & offset) != 0) + return TRUE; + return FALSE; +} + +static gboolean +fu_d19_mst_check_fw (FuDevice *symbiote, GError **error) +{ + g_autoptr(GBytes) bytes = NULL; + const guint8 *data; + gsize length = 4; + + if (!fu_dell_dock_mst_read_register (symbiote, + MST_CORE_MCU_BOOTLOADER_STS, + length, &bytes, + error)) + return FALSE; + data = g_bytes_get_data (bytes, &length); + + g_debug ("MST: firmware check: %d", + fu_dell_dock_mst_check_offset (data[0], 0x01)); + g_debug ("MST: HDCP key check: %d", + fu_dell_dock_mst_check_offset (data[0], 0x02)); + g_debug ("MST: Config0 check: %d", + fu_dell_dock_mst_check_offset (data[0], 0x04)); + g_debug ("MST: Config1 check: %d", + fu_dell_dock_mst_check_offset (data[0], 0x08)); + + if (fu_dell_dock_mst_check_offset (data[0], 0xF0)) + g_debug ("MST: running in bootloader"); + else + g_debug ("MST: running in firmware"); + g_debug ("MST: Error code: %x", data[1]); + g_debug ("MST: GPIO boot strap record: %d", data[2]); + g_debug ("MST: Bootloader version number %x", data[3]); + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_checksum_bank (FuDevice *symbiote, + GBytes *blob_fw, + MSTBank bank, + gboolean *checksum, + GError **error) +{ + g_autoptr(GBytes) csum_bytes = NULL; + const MSTBankAttributes *attribs = NULL; + gsize length = 0; + const guint8 *data = g_bytes_get_data (blob_fw, &length); + guint32 payload_sum = 0; + guint32 bank_sum = 0; + + g_return_val_if_fail (blob_fw != NULL, FALSE); + g_return_val_if_fail (checksum != NULL, FALSE); + + if (!fu_dell_dock_mst_get_bank_attribs (bank, &attribs, error)) + return FALSE; + + /* bank is specified outside of payload */ + if (attribs->start + attribs->length > length) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Payload %u is bigger than bank %u", + attribs->start + attribs->length, bank); + return FALSE; + } + + /* checksum the file */ + for (guint i = attribs->start; i < attribs->length + attribs->start; + i++) { + payload_sum += data[i]; + } + g_debug ("MST: Payload checksum: 0x%x", payload_sum); + + /* checksum the bank */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_CHECKSUM, + attribs->length, attribs->start, + NULL, + error)) { + g_prefix_error (error, "Failed to checksum bank %u: ", bank); + return FALSE; + } + /* read result from data register */ + if (!fu_dell_dock_mst_read_register (symbiote, + MST_RC_DATA_ADDR, + 4, &csum_bytes, error)) + return FALSE; + data = g_bytes_get_data (csum_bytes, NULL); + bank_sum = GUINT32_FROM_LE (data[0] | data[1] << 8 | data[2] << 16 | + data[3] << 24); + g_debug ("MST: Bank %u checksum: 0x%x", bank, bank_sum); + + *checksum = (bank_sum == payload_sum); + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_erase_bank (FuDevice *symbiote, MSTBank bank, GError **error) +{ + const MSTBankAttributes *attribs = NULL; + guint32 sector; + + if (!fu_dell_dock_mst_get_bank_attribs (bank, &attribs, error)) + return FALSE; + + for (guint32 i = attribs->start; i < attribs->start + attribs->length; + i += 0x10000) { + sector = FLASH_SECTOR_ERASE_64K | (i / 0x10000); + g_debug ("MST: Erasing sector 0x%x", sector); + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_ERASE_FLASH, + 4, 0, + (guint8 *) §or, + error)) { + g_prefix_error ( + error, "Failed to erase sector 0x%x: ", sector); + return FALSE; + } + } + g_debug ("MST: Waiting for flash clear to settle"); + g_usleep (5000000); + + return TRUE; +} + +static gboolean +fu_dell_dock_write_flash_bank (FuDevice *device, + GBytes *blob_fw, + MSTBank bank, + GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST (device); + const MSTBankAttributes *attribs = NULL; + gsize write_size = 32; + guint end; + const guint8 *data = g_bytes_get_data (blob_fw, NULL); + + g_return_val_if_fail (blob_fw != NULL, FALSE); + + if (!fu_dell_dock_mst_get_bank_attribs (bank, &attribs, error)) + return FALSE; + end = attribs->start + attribs->length; + + g_debug ("MST: Writing payload to bank %u", bank); + for (guint i = attribs->start; i < end; i += write_size) { + if (!fu_dell_dock_mst_rc_command (self->symbiote, + MST_CMD_WRITE_FLASH, + write_size, i, + data + i, + error)) { + g_prefix_error ( + error, + "Failed to write bank %u payload offset 0x%x: ", + bank, i); + return FALSE; + } + fu_device_set_progress_full (device, + i - attribs->start, + end - attribs->start); + } + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_stop_esm (FuDevice *symbiote, GError **error) +{ + g_autoptr(GBytes) quad_bytes = NULL; + g_autoptr(GBytes) hdcp_bytes = NULL; + guint32 payload = 0x21; + gsize length = sizeof(guint32); + const guint8 *data; + guint8 data_out[sizeof(guint32)]; + + /* disable ESM first */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_WRITE_MEMORY, + length, MST_RC_TRIGGER_ADDR, + (guint8 *) &payload, + error)) + return FALSE; + + /* waiting for ESM exit */ + g_usleep(200); + + /* disable QUAD mode */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_READ_MEMORY, + length, MST_REG_QUAD_DISABLE, + NULL, + error)) + return FALSE; + + if (!fu_dell_dock_mst_read_register (symbiote, + MST_RC_DATA_ADDR, + length, &quad_bytes, + error)) + return FALSE; + + data = g_bytes_get_data (quad_bytes, &length); + memcpy (data_out, data, length); + data_out[0] = 0x00; + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_WRITE_MEMORY, + length, MST_REG_QUAD_DISABLE, + data_out, error)) + return FALSE; + + /* disable HDCP2.2 */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_READ_MEMORY, + length, MST_REG_HDCP22_DISABLE, + NULL, + error)) + return FALSE; + + if (!fu_dell_dock_mst_read_register (symbiote, + MST_RC_DATA_ADDR, + length, + &hdcp_bytes, + error)) + return FALSE; + + data = g_bytes_get_data (hdcp_bytes, &length); + memcpy (data_out, data, length); + data_out[0] = data[0] & (1 << 2); + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_WRITE_MEMORY, + length, MST_REG_HDCP22_DISABLE, + data_out, + error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_invalidate_bank (FuDevice *symbiote, MSTBank bank_in_use, + GError **error) +{ + const MSTBankAttributes *attribs; + g_autoptr(GBytes) bytes = NULL; + const guint8 *crc_tag; + const guint8 *new_tag; + guint32 crc_offset; + guint retries = 2; + + if (!fu_dell_dock_mst_get_bank_attribs (bank_in_use, &attribs, error)) { + g_prefix_error (error, "unable to invalidate bank: "); + return FALSE; + } + /* we need to write 4 byte increments over I2C so this differs from DP aux */ + crc_offset = attribs->start + EEPROM_TAG_OFFSET + 12; + + /* Read CRC byte to flip */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_READ_FLASH, + 4, crc_offset, + NULL, + error)) { + + g_prefix_error (error, "failed to read tag from flash: "); + return FALSE; + } + if (!fu_dell_dock_mst_read_register (symbiote, + MST_RC_DATA_ADDR, + 1, + &bytes, + error)) { + return FALSE; + } + crc_tag = g_bytes_get_data (bytes, NULL); + g_debug ("CRC byte is currently 0x%x", crc_tag[3]); + + for (guint32 retries_cnt = 0; ; retries_cnt++) { + g_autoptr(GBytes) bytes_new = NULL; + /* CRC8 is not 0xff, erase last 4k of bank# */ + if (crc_tag[3] != 0xff) { + guint32 sector = FLASH_SECTOR_ERASE_4K + + (attribs->start + attribs->length - 0x1000) / 0x1000; + g_debug ("Erasing 4k from sector 0x%x invalidate bank %u", + sector, bank_in_use); + /* offset for last 4k of bank# */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_ERASE_FLASH, + 4, 0, + (guint8 *) §or, + error)) { + + g_prefix_error (error, + "failed to erase sector 0x%x: ", + sector); + return FALSE; + } + /* CRC8 is 0xff, set it to 0x00 */ + } else { + guint32 write = 0x00; + g_debug ("Writing 0x00 byte to 0x%x to invalidate bank %u", + crc_offset, bank_in_use); + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_WRITE_FLASH, + 4, crc_offset, + (guint8*) &write, + error)) { + + g_prefix_error (error, "failed to clear CRC byte: "); + return FALSE; + } + } + /* re-read for comparison */ + if (!fu_dell_dock_mst_rc_command (symbiote, + MST_CMD_READ_FLASH, + 4, crc_offset, + NULL, + error)) { + + g_prefix_error (error, "failed to read tag from flash: "); + return FALSE; + } + if (!fu_dell_dock_mst_read_register (symbiote, + MST_RC_DATA_ADDR, + 4, &bytes_new, + error)) { + return FALSE; + } + new_tag = g_bytes_get_data (bytes_new, NULL); + g_debug ("CRC byte is currently 0x%x", new_tag[3]); + + /* tag successfully cleared */ + if ((new_tag[3] == 0xff && crc_tag[3] != 0xff) || + (new_tag[3] == 0x00 && crc_tag[3] == 0xff)) { + break; + } + if (retries_cnt > retries) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "set tag invalid fail (new 0x%x; old 0x%x)", + new_tag[3], crc_tag[3]); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_write_fw (FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST (device); + MSTBank bank_in_use = 0; + guint retries = 2; + gboolean checksum = FALSE; + guint8 order[2] = {ESM, Bank0}; + guint16 chip_id; + const guint8* data = g_bytes_get_data (blob_fw, NULL); + g_autofree gchar *dynamic_version = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (blob_fw != NULL, FALSE); + g_return_val_if_fail (self->symbiote != NULL, FALSE); + + dynamic_version = g_strdup_printf ("%02x.%02x.%02x", + data[self->blob_major_offset], + data[self->blob_minor_offset], + data[self->blob_build_offset]); + g_debug ("writing MST firmware version %s", dynamic_version); + + /* determine the flash order */ + if (!fu_dell_dock_mst_query_active_bank (self->symbiote, &bank_in_use, error)) + return FALSE; + + if (bank_in_use == Bank0) + order[1] = Bank1; + + /* enable remote control */ + if (!fu_dell_dock_mst_enable_remote_control (self->symbiote, error)) + return FALSE; + + /* Read Synaptics MST chip ID */ + if (!fu_dell_dock_mst_read_chipid (self->symbiote, &chip_id, + error)) + return FALSE; + if (chip_id != EXPECTED_CHIPID) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unknown MST chip found %x", chip_id); + return FALSE; + } + + /* ESM needs special handling during flash process*/ + if (!fu_dell_dock_mst_stop_esm (self->symbiote, error)) + return FALSE; + + /* Write each bank in order */ + for (guint phase = 0; phase < 2; phase++) { + g_debug ("MST: Checking bank %u", order[phase]); + if (!fu_dell_dock_mst_checksum_bank (self->symbiote, + blob_fw, + order[phase], + &checksum, error)) + return FALSE; + if (checksum) { + g_debug ("MST: bank %u is already up to date", order[phase]); + continue; + } + g_debug ("MST: bank %u needs to be updated", order[phase]); + for (guint i = 0; i < retries; i++) { + fu_device_set_progress_full (device, 0, 100); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_dell_dock_mst_erase_bank (self->symbiote, + order[phase], + error)) + return FALSE; + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_dell_dock_write_flash_bank (device, blob_fw, + order[phase], error)) + return FALSE; + if (!fu_dell_dock_mst_checksum_bank (self->symbiote, + blob_fw, + order[phase], + &checksum, + error)) + return FALSE; + fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY); + if (!checksum) { + g_debug ( + "MST: Failed to verify checksum on bank %u", + order[phase]); + continue; + } + g_debug ("MST: Bank %u successfully flashed", order[phase]); + break; + } + /* failed after all our retries */ + if (!checksum) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to write to bank %u", order[phase]); + return FALSE; + } + } + /* invalidate the previous bank */ + if (!fu_dell_dock_mst_invalidate_bank (self->symbiote, bank_in_use, error)) { + g_prefix_error (error, "failed to invalidate bank %u: ", bank_in_use); + return FALSE; + } + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version (device, dynamic_version, FWUPD_VERSION_FORMAT_TRIPLET); + + /* disable remote control now */ + return fu_dell_dock_mst_disable_remote_control (self->symbiote, error); +} + +static gboolean +fu_dell_dock_mst_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST (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; + } + if (g_strcmp0 (key, "DellDockBlobMajorOffset") == 0) { + self->blob_major_offset = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "DellDockBlobMinorOffset") == 0) { + self->blob_minor_offset = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "DellDockBlobBuildOffset") == 0) { + self->blob_build_offset = fu_common_strtoull (value); + return TRUE; + } + else if (g_strcmp0 (key, "DellDockInstallDurationI2C") == 0) { + guint64 tmp = fu_common_strtoull (value); + fu_device_set_install_duration (device, tmp); + 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_mst_setup (FuDevice *device, GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST (device); + FuDevice *parent; + const gchar *version; + + /* sanity check that we can talk to MST */ + if (!fu_d19_mst_check_fw (self->symbiote, error)) + return FALSE; + + /* set version from EC if we know it */ + parent = fu_device_get_parent (device); + version = fu_dell_dock_ec_get_mst_version (parent); + if (version != NULL) + fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_TRIPLET); + + fu_dell_dock_clone_updatable (device); + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_probe (FuDevice *device, GError **error) +{ + fu_device_set_logical_id (FU_DEVICE (device), "mst"); + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_open (FuDevice *device, GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST (device); + FuDevice *parent = fu_device_get_parent (device); + + g_return_val_if_fail (self->unlock_target != 0, FALSE); + g_return_val_if_fail (parent != NULL, 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; + + /* open up access to controller bus */ + if (!fu_dell_dock_set_power (device, self->unlock_target, TRUE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_close (FuDevice *device, GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST (device); + + /* close access to controller bus */ + 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_mst_finalize (GObject *object) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST (object); + g_object_unref (self->symbiote); + G_OBJECT_CLASS (fu_dell_dock_mst_parent_class)->finalize (object); +} + +static void +fu_dell_dock_mst_init (FuDellDockMst *self) +{ +} + +static void +fu_dell_dock_mst_class_init (FuDellDockMstClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + object_class->finalize = fu_dell_dock_mst_finalize; + klass_device->probe = fu_dell_dock_mst_probe; + klass_device->open = fu_dell_dock_mst_open; + klass_device->close = fu_dell_dock_mst_close; + klass_device->setup = fu_dell_dock_mst_setup; + klass_device->probe = fu_dell_dock_mst_probe; + klass_device->write_firmware = fu_dell_dock_mst_write_fw; + klass_device->set_quirk_kv = fu_dell_dock_mst_set_quirk_kv; +} + +FuDellDockMst * +fu_dell_dock_mst_new (void) +{ + FuDellDockMst *device = NULL; + device = g_object_new (FU_TYPE_DELL_DOCK_MST, NULL); + return device; +} diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-mst.h fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-mst.h --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-mst.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-mst.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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 + */ + +#pragma once + +#include "config.h" + +#include "fu-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_DELL_DOCK_MST (fu_dell_dock_mst_get_type ()) +G_DECLARE_FINAL_TYPE (FuDellDockMst, fu_dell_dock_mst, FU, DELL_DOCK_MST, FuDevice) + +FuDellDockMst *fu_dell_dock_mst_new (void); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-tbt.c fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-tbt.c --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-tbt.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-tbt.c 2019-07-15 18:25:54.000000000 +0000 @@ -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, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); + guint32 start_offset = 0; + gsize image_size; + const guint8 *buffer = g_bytes_get_data (blob_fw, &image_size); + 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, FWUPD_VERSION_FORMAT_PAIR); + + 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, FWUPD_VERSION_FORMAT_PAIR); + + /* 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_instance_id (device, DELL_DOCK_TBT_INSTANCE_ID); + + 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 -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-tbt.h fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-tbt.h --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-i2c-tbt.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-i2c-tbt.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * 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 + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-status.c fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-status.c --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-status.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-status.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2018 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 "fu-dell-dock-common.h" + +struct _FuDellDockStatus { + FuDevice parent_instance; + guint64 blob_version_offset; +}; + +G_DEFINE_TYPE (FuDellDockStatus, fu_dell_dock_status, FU_TYPE_DEVICE) + +static gchar * +fu_dell_dock_status_ver_string (guint32 status_version) +{ + /* guint32 BCD */ + return g_strdup_printf ("%02x.%02x.%02x.%02x", + status_version & 0xff, + (status_version >> 8) & 0xff, + (status_version >> 16) & 0xff, + (status_version >> 24) & 0xff); +} + +static gboolean +fu_dell_dock_status_setup (FuDevice *device, GError **error) +{ + FuDevice *parent; + guint32 status_version; + g_autofree gchar *dynamic_version = NULL; + + parent = fu_device_get_parent (device); + status_version = fu_dell_dock_ec_get_status_version (parent); + + dynamic_version = fu_dell_dock_status_ver_string (status_version); + fu_device_set_version (device, dynamic_version, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_logical_id (FU_DEVICE (device), "status"); + + fu_dell_dock_clone_updatable (device); + + return TRUE; +} + +static gboolean +fu_dell_dock_status_write (FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockStatus *self = FU_DELL_DOCK_STATUS (device); + FuDevice *parent; + gsize length = 0; + guint32 status_version = 0; + const guint8 *data = g_bytes_get_data (blob_fw, &length); + g_autofree gchar *dynamic_version = NULL; + + g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (blob_fw != NULL, FALSE); + + memcpy (&status_version, data + self->blob_version_offset, sizeof (guint32)); + dynamic_version = fu_dell_dock_status_ver_string (status_version); + g_debug ("writing status firmware version %s", dynamic_version); + + parent = fu_device_get_parent (device); + if (!fu_dell_dock_ec_commit_package (parent, blob_fw, error)) + return FALSE; + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version (device, dynamic_version, FWUPD_VERSION_FORMAT_QUAD); + return TRUE; +} + +static gboolean +fu_dell_dock_status_open (FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent (device); + + g_return_val_if_fail (parent != NULL, FALSE); + + return fu_device_open (parent, error); +} + +static gboolean +fu_dell_dock_status_close (FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent (device); + + return fu_device_close (parent, error); +} + +static gboolean +fu_dell_dock_status_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDellDockStatus *self = FU_DELL_DOCK_STATUS (device); + if (g_strcmp0 (key, "DellDockBlobVersionOffset") == 0) { + self->blob_version_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 void +fu_dell_dock_status_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_dell_dock_status_parent_class)->finalize (object); +} + +static void +fu_dell_dock_status_init (FuDellDockStatus *self) +{ +} + +static void +fu_dell_dock_status_class_init (FuDellDockStatusClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + object_class->finalize = fu_dell_dock_status_finalize; + klass_device->write_firmware = fu_dell_dock_status_write; + klass_device->setup = fu_dell_dock_status_setup; + klass_device->open = fu_dell_dock_status_open; + klass_device->close = fu_dell_dock_status_close; + klass_device->set_quirk_kv = fu_dell_dock_status_set_quirk_kv; +} + +FuDellDockStatus * +fu_dell_dock_status_new (void) +{ + FuDellDockStatus *self = NULL; + self = g_object_new (FU_TYPE_DELL_DOCK_STATUS, NULL); + return self; +} diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-status.h fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-status.h --- fwupd-1.0.9/plugins/dell-dock/fu-dell-dock-status.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-dell-dock-status.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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 + */ + +#pragma once + +#include "config.h" + +#include "fu-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_DELL_DOCK_STATUS (fu_dell_dock_status_get_type ()) +G_DECLARE_FINAL_TYPE (FuDellDockStatus, fu_dell_dock_status, FU, DELL_DOCK_STATUS, FuDevice) + +FuDellDockStatus *fu_dell_dock_status_new (void); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/dell-dock/fu-plugin-dell-dock.c fwupd-1.2.10/plugins/dell-dock/fu-plugin-dell-dock.c --- fwupd-1.0.9/plugins/dell-dock/fu-plugin-dell-dock.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/fu-plugin-dell-dock.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2018 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 "fu-device.h" +#include "fwupd-error.h" +#include "fu-plugin-vfuncs.h" + +#include "fu-dell-dock-common.h" + +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); + g_type_ensure (FU_TYPE_DELL_DOCK_MST); + + /* 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 +fu_plugin_dell_dock_create_node (FuPlugin *plugin, + FuDevice *device, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + fu_device_set_quirks (device, fu_plugin_get_quirks (plugin)); + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + fu_plugin_device_add (plugin, device); + + return TRUE; +} + +static gboolean +fu_plugin_dell_dock_probe (FuPlugin *plugin, + FuDevice *symbiote, + GError **error) +{ + g_autoptr(FuDellDockEc) ec_device = NULL; + + /* create all static endpoints */ + ec_device = fu_dell_dock_ec_new (symbiote); + if (!fu_plugin_dell_dock_create_node (plugin, + FU_DEVICE (ec_device), + 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; +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, + FuUsbDevice *device, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDellDockHub) hub = fu_dell_dock_hub_new (device); + FuDevice *fu_device = FU_DEVICE (hub); + const gchar *key = NULL; + + locker = fu_device_locker_new (fu_device, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, fu_device); + + if (fu_device_has_custom_flag (fu_device, "has-bridge")) { + g_autoptr(GError) error_local = NULL; + + /* only add the device with parent to cache */ + key = fu_device_get_id (fu_device); + if (fu_plugin_cache_lookup (plugin, key) != NULL) { + g_debug ("Ignoring already added device %s", key); + return TRUE; + } + fu_plugin_cache_add (plugin, key, fu_device); + + /* probe for extended devices */ + if (!fu_plugin_dell_dock_probe (plugin, + fu_device, + &error_local)) { + g_warning ("Failed to probe bridged devices for %s: %s", + key, + error_local->message); + } + } + + /* clear updatable flag if parent doesn't have it */ + fu_dell_dock_clone_updatable (fu_device); + + return TRUE; +} + +gboolean +fu_plugin_device_removed (FuPlugin *plugin, FuDevice *device, GError **error) +{ + const gchar *device_key = fu_device_get_id (device); + FuDevice *dev; + FuDevice *parent; + + /* only the device with bridge will be in cache */ + dev = fu_plugin_cache_lookup (plugin, device_key); + if (dev == NULL) + return TRUE; + fu_plugin_cache_remove (plugin, device_key); + + /* find the parent and ask daemon to remove whole chain */ + parent = fu_device_get_parent (dev); + if (parent != NULL && FU_IS_DELL_DOCK_EC (parent)) { + g_debug ("Removing %s (%s)", + fu_device_get_name (parent), + fu_device_get_id (parent)); + fu_plugin_device_remove (plugin, parent); + } + + return TRUE; +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *dev, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + + fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_device_write_firmware (dev, blob_fw, flags, error)) { + g_prefix_error (error, + "failed to update %s: ", + fu_device_get_name (dev)); + return FALSE; + } + fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART); + + return TRUE; +} + +/* prefer to use EC if in the transaction and parent if it is not */ +static FuDevice * +fu_plugin_dell_dock_get_ec (GPtrArray *devices) +{ + FuDevice *ec_parent = NULL; + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index (devices, i); + FuDevice *parent; + if (FU_IS_DELL_DOCK_EC (dev)) + return dev; + parent = fu_device_get_parent (dev); + if (parent != NULL && FU_IS_DELL_DOCK_EC (parent)) + ec_parent = parent; + } + + return ec_parent; +} + +gboolean +fu_plugin_composite_prepare (FuPlugin *plugin, + GPtrArray *devices, + GError **error) +{ + FuDevice *parent = fu_plugin_dell_dock_get_ec (devices); + gboolean remaining_replug = FALSE; + + if (parent == NULL) + return TRUE; + + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index (devices, i); + /* if thunderbolt is part of transaction our family is leaving us */ + if (g_strcmp0 (fu_device_get_plugin (dev), "thunderbolt") == 0) { + if (fu_device_get_parent (dev) != parent) + continue; + fu_dell_dock_will_replug (parent); + /* set all other devices to replug */ + remaining_replug = TRUE; + continue; + } + /* different device */ + if (fu_device_get_parent (dev) != parent) + continue; + if (remaining_replug) + fu_dell_dock_will_replug (dev); + } + + return TRUE; +} + +gboolean +fu_plugin_composite_cleanup (FuPlugin *plugin, + GPtrArray *devices, + GError **error) +{ + FuDevice *parent = fu_plugin_dell_dock_get_ec (devices); + g_autoptr(FuDeviceLocker) locker = NULL; + + if (parent == NULL) + return TRUE; + + locker = fu_device_locker_new (parent, error); + if (locker == NULL) + return FALSE; + + return fu_dell_dock_ec_reboot_dock (parent, error); +} + +gboolean +fu_plugin_activate (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + if (!FU_IS_DELL_DOCK_EC (device)) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, + "Invalid device to activate"); + return FALSE; + } + + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + return fu_device_activate (device, error); +} diff -Nru fwupd-1.0.9/plugins/dell-dock/meson.build fwupd-1.2.10/plugins/dell-dock/meson.build --- fwupd-1.0.9/plugins/dell-dock/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,34 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginDellDock"'] + +install_data(['dell-dock.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_dell_dock', + fu_hash, + sources : [ + 'fu-plugin-dell-dock.c', + 'fu-dell-dock-common.c', + 'fu-dell-dock-hid.c', + '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 : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + gudev, + ], +) diff -Nru fwupd-1.0.9/plugins/dell-dock/README.md fwupd-1.2.10/plugins/dell-dock/README.md --- fwupd-1.0.9/plugins/dell-dock/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-dock/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,71 @@ +Dell USB-C Dock +========= + +### Dell System +Unlike previous Dell USB-C devices, a Dell system is not needed for updating. + +### Components +The device contains components the following directly updatable components: +* USB hubs +* MST controller +* Thunderbolt controller +* Embedded controller + +This plugin is used to perform the update on the USB hubs as well as the Dell +Embedded controller. The USB hubs are updated directly over a USB HID endpoint +while the embedded controller is updated using an I2C over HID interface. + +The fwupd thunderbolt plugin is used for updating the Titan Ridge controller. + +The MST controller is updated through either the DP Aux interface +(SynapticsMST plugin) or I2C over HID interface provided by this plugin. + +## Device topology +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 -Nru fwupd-1.0.9/plugins/dell-esrt/dell-esrt.conf fwupd-1.2.10/plugins/dell-esrt/dell-esrt.conf --- fwupd-1.0.9/plugins/dell-esrt/dell-esrt.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-esrt/dell-esrt.conf 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,8 @@ +[fwupd Remote] + +# this remote provides metadata shipped with the fwupd package +Enabled=true +Title=Enable UEFI capsule updates on Dell systems +Keyring=none +MetadataURI=file://@datadir@/fwupd/remotes.d/dell-esrt/metadata.xml +ApprovalRequired=false diff -Nru fwupd-1.0.9/plugins/dell-esrt/fu-plugin-dell-esrt.c fwupd-1.2.10/plugins/dell-esrt/fu-plugin-dell-esrt.c --- fwupd-1.0.9/plugins/dell-esrt/fu-plugin-dell-esrt.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-esrt/fu-plugin-dell-esrt.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2017-2018 Dell, Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include "fu-plugin-vfuncs.h" + +/* Whitelisted smbios class/select commands */ +#define CLASS_ADMIN_PROP 10 +#define SELECT_ADMIN_PROP 3 + +/* whitelisted tokens */ +#define CAPSULE_EN_TOKEN 0x0461 +#define CAPSULE_DIS_TOKEN 0x0462 + +/* these aren't defined upstream but used in fwupdate */ +#define DELL_ADMIN_MASK 0xF +#define DELL_ADMIN_INSTALLED 0 + +static gboolean +fu_plugin_dell_esrt_query_token (guint16 token, gboolean *value, GError **error) +{ + if (!token_is_bool (token)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "token %" G_GUINT16_FORMAT " is not boolean", + token); + return FALSE; + } + if (value != NULL) + *value = token_is_active (token) > 0; + + return TRUE; +} + +static gboolean +fu_plugin_dell_esrt_activate_token (guint16 token, GError **error) +{ + token_activate (token); + if (token_is_active (token) < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "token %" G_GUINT16_FORMAT "cannot be activated " + "as the password is set", token); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_plugin_dell_esrt_admin_password_present (gboolean *password_present, GError **error) +{ + guint32 args[4] = { 0, }, out[4] = { 0, }; + + if (dell_simple_ci_smi (CLASS_ADMIN_PROP, + SELECT_ADMIN_PROP, args, out)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot call SMI for CLASS_ADMIN_PROP"); + return FALSE; + } + + if (out[0] != 0 || (out[1] & DELL_ADMIN_MASK) == DELL_ADMIN_INSTALLED) { + *password_present = TRUE; + } else { + *password_present = FALSE; + } + 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) +{ + gboolean capsule_disable = FALSE; + g_autofree gchar *sysfsfwdir = NULL; + g_autofree gchar *esrtdir = NULL; + + /* already exists */ + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + esrtdir = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); + if (g_file_test (esrtdir, G_FILE_TEST_EXISTS)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "UEFI firmware already supported"); + return FALSE; + } + + /* is the capsule functionality disabled */ + if (!fu_plugin_dell_esrt_query_token (CAPSULE_DIS_TOKEN, &capsule_disable, error)) + return FALSE; + if (!capsule_disable) { + gboolean capsule_enable = FALSE; + if (!fu_plugin_dell_esrt_query_token (CAPSULE_EN_TOKEN, &capsule_enable, error)) + return FALSE; + if (capsule_enable) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "UEFI firmware will be unlocked on next boot"); + return FALSE; + } + } + + return TRUE; +} + +gboolean +fu_plugin_unlock (FuPlugin *plugin, FuDevice *device, GError **error) +{ + gboolean password_present = FALSE; + /* check the admin password isn't set */ + if (!fu_plugin_dell_esrt_admin_password_present (&password_present, error)) + return FALSE; + if (password_present) { + const gchar *err_string = "Cannot be unlocked automatically as admin password set"; + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + err_string); + fu_device_set_update_error (device, err_string); + return FALSE; + } + + /* disabled in BIOS, but supported to be enabled via tool */ + if (!fu_plugin_dell_esrt_query_token (CAPSULE_EN_TOKEN, NULL, error)) + return FALSE; + if (!fu_plugin_dell_esrt_activate_token (CAPSULE_EN_TOKEN, error)) + return FALSE; + fu_device_set_update_error (device, NULL); + + return TRUE; +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + g_autoptr(FuDevice) dev = fu_device_new (); + + /* create a dummy device so we can unlock the feature */ + fu_device_set_id (dev, "UEFI-dummy-dev0"); + fu_device_set_name (dev, "Dell UEFI updates"); + fu_device_set_summary (dev, "Enable UEFI Update Functionality"); + fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); + fu_device_set_version (dev, "0", FWUPD_VERSION_FORMAT_NUMBER); + fu_device_add_icon (dev, "computer"); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_LOCKED); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_set_update_error (dev, "Firmware updates disabled; run 'fwupdmgr unlock' to enable"); + fu_plugin_device_add (plugin, dev); + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/dell-esrt/meson.build fwupd-1.2.10/plugins/dell-esrt/meson.build --- fwupd-1.0.9/plugins/dell-esrt/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-esrt/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,40 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginDellEsrt"'] + +install_data(['metadata.xml'], + install_dir : join_paths(datadir, 'fwupd', 'remotes.d', 'dell-esrt') +) + +shared_module('fu_plugin_dell_esrt', + fu_hash, + sources : [ + 'fu-plugin-dell-esrt.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + c_args : [ + cargs, + ], + link_with : [ + libfwupdprivate, + ], + dependencies : [ + plugin_deps, + libsmbios_c, + ], +) + +# replace @datadir@ +con2 = configuration_data() +con2.set('datadir', datadir) +configure_file( + input : 'dell-esrt.conf', + output : 'dell-esrt.conf', + configuration : con2, + install: true, + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), +) diff -Nru fwupd-1.0.9/plugins/dell-esrt/metadata.xml fwupd-1.2.10/plugins/dell-esrt/metadata.xml --- fwupd-1.0.9/plugins/dell-esrt/metadata.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-esrt/metadata.xml 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,27 @@ + + + + + + org.fwupd.8330a096d9f1af8567c7374cb8403e1ce9cf3163.device + + 2d47f29b-83a2-4f31-a2e8-63474f4d4c2e + + UEFI Updates + Enable UEFI Update Functionality + + + +

+ Applying this update will enable the UEFI firmware reporting interface on your hardware. +

+

+ You will have to restart your computer after this update is installed + to be notified of any pending firmware updates. +

+
+
+
+
+ +
diff -Nru fwupd-1.0.9/plugins/dell-esrt/README.md fwupd-1.2.10/plugins/dell-esrt/README.md --- fwupd-1.0.9/plugins/dell-esrt/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/dell-esrt/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,42 @@ +Dell ESRT Support +================= + +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 +------------------ + +For Dell support you will need libsmbios_c version 2.4.0 or later. + +* source: https://github.com/dell/libsmbios +* binaries: https://github.com/dell/libsmbios/releases + +If you don't want or need this functionality you can use the +`-Dplugin_dell=false` option. + +# Devices powered by the Dell Plugin +The Dell ESRT plugin allows the user to enable the UpdateCapsule functionality +at runtime. A reboot will be required and the BIOS administrator password +must not be set. + +Machines that offer this functionality will display an extra device in +```# fwupdmgr get-devices``` output. + +Example: +``` +UEFI dummy device + Guid: 2d47f29b-83a2-4f31-a2e8-63474f4d4c2e + Plugin: dell-esrt + Flags: internal|updatable|locked + Version: 0 + Created: 2018-06-25 +``` diff -Nru fwupd-1.0.9/plugins/dfu/contrib/parse-avrdude-conf.py fwupd-1.2.10/plugins/dfu/contrib/parse-avrdude-conf.py --- fwupd-1.0.9/plugins/dfu/contrib/parse-avrdude-conf.py 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/contrib/parse-avrdude-conf.py 2019-07-15 18:25:54.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/python3 """ This parses avrdude.conf and generates quirks for fwupd """ # pylint: disable=wrong-import-position,pointless-string-statement diff -Nru fwupd-1.0.9/plugins/dfu/dfu-chunked.c fwupd-1.2.10/plugins/dfu/dfu-chunked.c --- fwupd-1.0.9/plugins/dfu/dfu-chunked.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-chunked.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 20157 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "dfu-chunked.h" - -/** - * dfu_chunked_packet_new: - * @idx: the packet number - * @page: the hardware memory page - * @address: the address *within* the page - * @data: the data - * @data_sz: size of @data_sz - * - * Creates a new packet of chunked data. - * - * Return value: (transfer full): a #DfuChunkedPacket - **/ -DfuChunkedPacket * -dfu_chunked_packet_new (guint32 idx, - guint32 page, - guint32 address, - const guint8 *data, - guint32 data_sz) -{ - DfuChunkedPacket *item = g_new0 (DfuChunkedPacket, 1); - item->idx = idx; - item->page = page; - item->address = address; - item->data = data; - item->data_sz = data_sz; - return item; -} - -/** - * dfu_chunked_packet_to_string: - * @item: a #DfuChunkedPacket - * - * Converts the chunked packet to a string representation. - * - * Return value: (transfer full): A string - **/ -gchar * -dfu_chunked_packet_to_string (DfuChunkedPacket *item) -{ - g_autoptr(GString) str = g_string_new (NULL); - if (item->data != NULL) { - for (guint32 i = 0; i < item->data_sz; i++) { - gchar tmp = (gchar) item->data[i]; - if (tmp == 0x00) - break; - g_string_append_c (str, g_ascii_isalnum (tmp) ? tmp : '?'); - } - } - return g_strdup_printf ("#%02" G_GUINT32_FORMAT ": page:%02x " - "addr:%04x len:%02" G_GUINT32_FORMAT " %s", - item->idx, - (guint) item->page, - (guint) item->address, - item->data_sz, - str->str); -} - -/** - * dfu_chunked_to_string: - * @segments: (element-type DfuChunkedPacket): array of packets - * - * Converts all the chunked packets in an array to a string representation. - * - * Return value: (transfer full): A string - **/ -gchar * -dfu_chunked_to_string (GPtrArray *segments) -{ - GString *str = g_string_new (NULL); - for (guint i = 0; i < segments->len; i++) { - DfuChunkedPacket *item = g_ptr_array_index (segments, i); - g_autofree gchar *tmp = dfu_chunked_packet_to_string (item); - g_string_append_printf (str, "%s\n", tmp); - } - return g_string_free (str, FALSE); -} - -/** - * dfu_chunked_new: - * @data: a linear blob of memory, or %NULL - * @data_sz: size of @data_sz - * @addr_start: the hardware address offset, or 0 - * @page_sz: the hardware page size, or 0 - * @packet_sz: the transfer size, or 0 - * - * Chunks a linear blob of memory into packets, ensuring each packet does not - * cross a package boundary and is less that a specific transfer size. - * - * Return value: (element-type DfuChunkedPacket): array of packets - **/ -GPtrArray * -dfu_chunked_new (const guint8 *data, - guint32 data_sz, - guint32 addr_start, - guint32 page_sz, - guint32 packet_sz) -{ - GPtrArray *segments = NULL; - guint32 page_old = G_MAXUINT32; - guint32 idx; - guint32 last_flush = 0; - - g_return_val_if_fail (data_sz > 0, NULL); - - segments = g_ptr_array_new_with_free_func (g_free); - for (idx = 1; idx < data_sz; idx++) { - guint32 page = 0; - if (page_sz > 0) - page = (addr_start + idx) / page_sz; - if (page_old == G_MAXUINT32) { - page_old = page; - } else if (page != page_old) { - const guint8 *data_offset = data != NULL ? data + last_flush : 0x0; - guint32 address_offset = addr_start + last_flush; - if (page_sz > 0) - address_offset %= page_sz; - g_ptr_array_add (segments, - dfu_chunked_packet_new (segments->len, - page_old, - address_offset, - data_offset, - idx - last_flush)); - last_flush = idx; - page_old = page; - continue; - } - if (packet_sz > 0 && idx - last_flush >= packet_sz) { - const guint8 *data_offset = data != NULL ? data + last_flush : 0x0; - guint32 address_offset = addr_start + last_flush; - if (page_sz > 0) - address_offset %= page_sz; - g_ptr_array_add (segments, - dfu_chunked_packet_new (segments->len, - page, - address_offset, - data_offset, - idx - last_flush)); - last_flush = idx; - continue; - } - } - if (last_flush != idx) { - const guint8 *data_offset = data != NULL ? data + last_flush : 0x0; - guint32 address_offset = addr_start + last_flush; - guint32 page = 0; - if (page_sz > 0) { - address_offset %= page_sz; - page = (addr_start + (idx - 1)) / page_sz; - } - g_ptr_array_add (segments, - dfu_chunked_packet_new (segments->len, - page, - address_offset, - data_offset, - data_sz - last_flush)); - } - return segments; -} - -/** - * dfu_chunked_new_from_bytes: - * @blob: a #GBytes - * @addr_start: the hardware address offset, or 0 - * @page_sz: the hardware page size, or 0 - * @packet_sz: the transfer size, or 0 - * - * Chunks a linear blob of memory into packets, ensuring each packet does not - * cross a package boundary and is less that a specific transfer size. - * - * Return value: (element-type DfuChunkedPacket): array of packets - **/ -GPtrArray * -dfu_chunked_new_from_bytes (GBytes *blob, - guint32 addr_start, - guint32 page_sz, - guint32 packet_sz) -{ - gsize sz; - const guint8 *data = g_bytes_get_data (blob, &sz); - return dfu_chunked_new (data, (guint32) sz, - addr_start, page_sz, packet_sz); -} diff -Nru fwupd-1.0.9/plugins/dfu/dfu-chunked.h fwupd-1.2.10/plugins/dfu/dfu-chunked.h --- fwupd-1.0.9/plugins/dfu/dfu-chunked.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-chunked.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __DFU_CHUNKED_H -#define __DFU_CHUNKED_H - -#include -#include - -G_BEGIN_DECLS - -typedef struct { - guint32 idx; - guint32 page; - guint32 address; - const guint8 *data; - guint32 data_sz; -} DfuChunkedPacket; - -DfuChunkedPacket *dfu_chunked_packet_new (guint32 idx, - guint32 page, - guint32 address, - const guint8 *data, - guint32 data_sz); -gchar *dfu_chunked_packet_to_string (DfuChunkedPacket *item); - -gchar *dfu_chunked_to_string (GPtrArray *chunked); -GPtrArray *dfu_chunked_new (const guint8 *data, - guint32 data_sz, - guint32 addr_start, - guint32 page_sz, - guint32 packet_sz); -GPtrArray *dfu_chunked_new_from_bytes (GBytes *blob, - guint32 addr_start, - guint32 page_sz, - guint32 packet_sz); - -G_END_DECLS - -#endif /* __DFU_CHUNKED_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-cipher-xtea.c fwupd-1.2.10/plugins/dfu/dfu-cipher-xtea.c --- fwupd-1.0.9/plugins/dfu/dfu-cipher-xtea.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-cipher-xtea.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-cipher-xtea.h fwupd-1.2.10/plugins/dfu/dfu-cipher-xtea.h --- fwupd-1.0.9/plugins/dfu/dfu-cipher-xtea.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-cipher-xtea.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FORMAT_XTEA_H -#define __DFU_FORMAT_XTEA_H +#pragma once #include #include @@ -23,5 +21,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_FORMAT_XTEA_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-common.c fwupd-1.2.10/plugins/dfu/dfu-common.c --- fwupd-1.0.9/plugins/dfu/dfu-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -179,26 +178,6 @@ } /** - * dfu_utils_bytes_is_empty: - * @bytes: a #GBytes - * - * Checks if a byte array are just empty (0xff) bytes. - * - * Return value: %TRUE if @bytes is empty - **/ -gboolean -dfu_utils_bytes_is_empty (GBytes *bytes) -{ - gsize sz = 0; - const guint8 *buf = g_bytes_get_data (bytes, &sz); - for (gsize i = 0; i < sz; i++) { - if (buf[i] != 0xff) - return FALSE; - } - return TRUE; -} - -/** * dfu_utils_bytes_pad: * @bytes: a #GBytes * @sz: the desired size in bytes @@ -327,3 +306,27 @@ 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 -Nru fwupd-1.0.9/plugins/dfu/dfu-common.h fwupd-1.2.10/plugins/dfu/dfu-common.h --- fwupd-1.0.9/plugins/dfu/dfu-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_COMMON_H -#define __DFU_COMMON_H +#pragma once #include #include @@ -117,7 +115,7 @@ * @DFU_CIPHER_KIND_XTEA: XTEA cipher detected * @DFU_CIPHER_KIND_RSA: RSA cipher detected * - * The type of cipher used for transfering the firmware. + * The type of cipher used for transferring the firmware. **/ typedef enum { DFU_CIPHER_KIND_NONE, @@ -158,7 +156,6 @@ /* helpers */ GBytes *dfu_utils_bytes_join_array (GPtrArray *chunks); -gboolean dfu_utils_bytes_is_empty (GBytes *bytes); GBytes *dfu_utils_bytes_pad (GBytes *bytes, gsize sz); guint8 dfu_utils_buffer_parse_uint4 (const gchar *data); @@ -166,7 +163,9 @@ 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 - -#endif /* __DFU_COMMON_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-device.c fwupd-1.2.10/plugins/dfu/dfu-device.c --- fwupd-1.0.9/plugins/dfu/dfu-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -21,6 +20,61 @@ * See also: #DfuTarget, #DfuFirmware */ +/** + * FU_QUIRKS_DFU_FLAGS: + * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` + * @value: a string, separated using `|`, e.g. `ignore-polltimeout|no-pid-change` + * + * Assigns optional quirks to use for a DFU device which does not follow the + * DFU 1.0 or 1.1 specification. The list of supported quirks is thus: + * + * * `none`: No device quirks + * * `action-required`: User has to do something manually, e.g. press a button + * * `attach-extra-reset`: Device needs resetting twice for attach + * * `attach-upload-download`: An upload or download is required for attach + * * `force-dfu-mode`: Force DFU mode + * * `ignore-polltimeout`: Ignore the device download timeout + * * `ignore-runtime`: Device has broken DFU runtime support + * * `ignore-upload`: Uploading from the device is broken + * * `no-dfu-runtime`: No DFU runtime interface is provided + * * `no-get-status-upload`: Do not do GetStatus when uploading + * * `no-pid-change`: Accept the same VID:PID when changing modes + * * `use-any-interface`: Use any interface for DFU + * * `use-atmel-avr`: Device uses the ATMEL bootloader + * * `use-protocol-zero`: Fix up the protocol number + * * `legacy-protocol`: Use a legacy protocol version + * + * Default value: `none` + * + * Since: 1.0.1 + */ +#define FU_QUIRKS_DFU_FLAGS "DfuFlags" + +/** + * FU_QUIRKS_DFU_FORCE_VERSION: + * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` + * @value: the uint16_t DFU version, encoded in base 16, e.g. `0110` + * + * 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. + * + * Since: 1.0.1 + */ +#define FU_QUIRKS_DFU_FORCE_VERSION "DfuForceVersion" + +/** + * FU_QUIRKS_DFU_JABRA_DETACH: + * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` + * @value: the two uint8_t unlock values, encoded in base 16, e.g. `0201` + * + * Assigns the two magic bytes sent to the Jabra hardware when the device is + * in runtime mode to make it switch into DFU mode. + * + * Since: 1.0.1 + */ +#define FU_QUIRKS_DFU_JABRA_DETACH "DfuJabraDetach" + #include "config.h" #include @@ -47,7 +101,9 @@ gboolean done_upload_or_download; gboolean claimed_interface; gchar *chip_id; + gchar *jabra_detach; guint16 version; + guint16 force_version; guint16 runtime_pid; guint16 runtime_vid; guint16 runtime_release; @@ -68,65 +124,6 @@ G_DEFINE_TYPE_WITH_PRIVATE (DfuDevice, dfu_device, FU_TYPE_USB_DEVICE) #define GET_PRIVATE(o) (dfu_device_get_instance_private (o)) -static gboolean dfu_device_open (FuUsbDevice *device, GError **error); -static gboolean dfu_device_close (FuUsbDevice *device, GError **error); -static gboolean dfu_device_probe (FuUsbDevice *device, GError **error); - -static void -dfu_device_class_init (DfuDeviceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); - klass_usb_device->open = dfu_device_open; - klass_usb_device->close = dfu_device_close; - klass_usb_device->probe = dfu_device_probe; - - /** - * DfuDevice::status-changed: - * @device: the #DfuDevice instance that emitted the signal - * @status: the new #DfuStatus - * - * The ::status-changed signal is emitted when the status changes. - **/ - signals [SIGNAL_STATUS_CHANGED] = - g_signal_new ("status-changed", - G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (DfuDeviceClass, status_changed), - NULL, NULL, g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - - /** - * DfuDevice::state-changed: - * @device: the #DfuDevice instance that emitted the signal - * @state: the new #DfuState - * - * The ::state-changed signal is emitted when the state changes. - **/ - signals [SIGNAL_STATE_CHANGED] = - g_signal_new ("state-changed", - G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (DfuDeviceClass, state_changed), - NULL, NULL, g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - - object_class->finalize = dfu_device_finalize; -} - -static void -dfu_device_init (DfuDevice *device) -{ - DfuDevicePrivate *priv = GET_PRIVATE (device); - priv->iface_number = 0xff; - priv->runtime_pid = 0xffff; - priv->runtime_vid = 0xffff; - priv->runtime_release = 0xffff; - priv->state = DFU_STATE_APP_IDLE; - priv->status = DFU_STATUS_OK; - priv->targets = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - priv->timeout_ms = 1500; - priv->transfer_size = 64; -} - /** * dfu_device_get_transfer_size: * @device: a #GUsbDevice @@ -190,20 +187,6 @@ priv->transfer_size = transfer_size; } -static void -dfu_device_finalize (GObject *object) -{ - DfuDevice *device = DFU_DEVICE (object); - DfuDevicePrivate *priv = GET_PRIVATE (device); - - if (priv->usb_context != NULL) - g_object_unref (priv->usb_context); - g_free (priv->chip_id); - g_ptr_array_unref (priv->targets); - - G_OBJECT_CLASS (dfu_device_parent_class)->finalize (object); -} - typedef struct __attribute__((packed)) { guint8 bLength; guint8 bDescriptorType; @@ -217,7 +200,7 @@ dfu_device_parse_iface_data (DfuDevice *device, GBytes *iface_data, GError **error) { DfuDevicePrivate *priv = GET_PRIVATE (device); - DfuFuncDescriptor desc; + DfuFuncDescriptor desc = { 0x0 }; const guint8 *buf; gsize sz; @@ -225,6 +208,10 @@ buf = g_bytes_get_data (iface_data, &sz); if (sz == sizeof(DfuFuncDescriptor)) { memcpy (&desc, buf, sz); + } else if (sz > sizeof(DfuFuncDescriptor)) { + g_debug ("DFU interface with %" G_GSIZE_FORMAT " bytes vendor data", + sz - sizeof(DfuFuncDescriptor)); + memcpy (&desc, buf, sizeof(DfuFuncDescriptor)); } else if (sz == sizeof(DfuFuncDescriptor) - 2) { g_warning ("truncated DFU interface data, no bcdDFUVersion"); memcpy (&desc, buf, sz); @@ -244,16 +231,6 @@ return FALSE; } - /* check sanity */ - if (desc.bLength != sz) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "DFU interface data has incorrect length: 0x%02x", - desc.bLength); - return FALSE; - } - /* get transfer size and version */ priv->transfer_size = GUINT16_FROM_LE (desc.wTransferSize); priv->version = GUINT16_FROM_LE (desc.bcdDFUVersion); @@ -300,7 +277,6 @@ { DfuDevicePrivate *priv = GET_PRIVATE (device); GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); - FuQuirks *system_quirks = fu_device_get_quirks (FU_DEVICE (device)); g_autoptr(GPtrArray) ifaces = NULL; /* add all DFU-capable targets */ @@ -311,7 +287,6 @@ for (guint i = 0; i < ifaces->len; i++) { GBytes *iface_data = NULL; DfuTarget *target; - const gchar *quirk_str; g_autoptr(GError) error_local = NULL; GUsbInterface *iface = g_ptr_array_index (ifaces, i); @@ -339,11 +314,8 @@ } /* fix up the version */ - quirk_str = fu_quirks_lookup_by_usb_device (system_quirks, - FU_QUIRKS_DFU_FORCE_VERSION, - usb_device); - if (quirk_str != NULL && strlen (quirk_str) == 4) - priv->version = dfu_utils_buffer_parse_uint16 (quirk_str); + if (priv->force_version > 0) + priv->version = priv->force_version; if (priv->version == DFU_VERSION_DFU_1_0 || priv->version == DFU_VERSION_DFU_1_1) { g_debug ("DFU v1.1"); @@ -403,17 +375,16 @@ } /* the device has no DFU runtime, so cheat */ - if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { - if (priv->targets->len == 0) { - g_debug ("no DFU runtime, so faking device"); - priv->state = DFU_STATE_APP_IDLE; - priv->iface_number = 0xff; - priv->runtime_vid = g_usb_device_get_vid (usb_device); - priv->runtime_pid = g_usb_device_get_pid (usb_device); - priv->runtime_release = g_usb_device_get_release (usb_device); - priv->attributes = DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD | - DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD; - } + if (priv->targets->len == 0 && + priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + g_debug ("no DFU runtime, so faking device"); + priv->state = DFU_STATE_APP_IDLE; + priv->iface_number = 0xff; + priv->runtime_vid = g_usb_device_get_vid (usb_device); + priv->runtime_pid = g_usb_device_get_pid (usb_device); + priv->runtime_release = g_usb_device_get_release (usb_device); + priv->attributes = DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD | + DFU_DEVICE_ATTRIBUTE_CAN_UPLOAD; return TRUE; } @@ -600,7 +571,8 @@ dfu_device_set_quirks_from_string (DfuDevice *device, const gchar *str) { DfuDevicePrivate *priv = GET_PRIVATE (device); - g_auto(GStrv) split = g_strsplit (str, "|", -1); + 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; @@ -653,23 +625,6 @@ } } -static void -dfu_device_apply_quirks (DfuDevice *device) -{ - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); - FuQuirks *system_quirks = fu_device_get_quirks (FU_DEVICE (device)); - if (system_quirks != NULL && usb_device != NULL) { - const gchar *quirk_str; - quirk_str = fu_quirks_lookup_by_usb_device (system_quirks, - FU_QUIRKS_DFU, - usb_device); - if (quirk_str != NULL) - dfu_device_set_quirks_from_string (device, quirk_str); - } else { - g_warning ("no system quirk information"); - } -} - void dfu_device_set_usb_context (DfuDevice *device, GUsbContext *quirks) { @@ -1039,7 +994,8 @@ } /* the device has no DFU runtime, so cheat */ - if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) + if (priv->state == DFU_STATE_APP_IDLE && + priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) return TRUE; /* ensure interface is claimed */ @@ -1074,7 +1030,8 @@ } /* some devices use the wrong state value */ - if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_FORCE_DFU_MODE)) { + if (dfu_device_has_quirk (device, DFU_DEVICE_QUIRK_FORCE_DFU_MODE) && + dfu_device_get_state (device) != DFU_STATE_DFU_IDLE) { g_debug ("quirking device into DFU mode"); dfu_device_set_state (device, DFU_STATE_DFU_IDLE); } else { @@ -1127,10 +1084,8 @@ dfu_device_detach (DfuDevice *device, GError **error) { DfuDevicePrivate *priv = GET_PRIVATE (device); - FuQuirks *system_quirks = fu_device_get_quirks (FU_DEVICE (device)); GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); const guint16 timeout_reset_ms = 1000; - const gchar *quirk_str; g_autoptr(GError) error_local = NULL; g_return_val_if_fail (DFU_IS_DEVICE (device), FALSE); @@ -1157,10 +1112,7 @@ } /* handle Jabra devices that need a magic HID packet */ - quirk_str = fu_quirks_lookup_by_usb_device (system_quirks, - FU_QUIRKS_DFU_JABRA_DETACH, - usb_device); - if (quirk_str != NULL) { + if (priv->jabra_detach != NULL) { guint8 adr = 0x00; guint8 rep = 0x00; guint8 iface_hid; @@ -1168,16 +1120,8 @@ g_autoptr(GError) error_jabra = NULL; /* parse string and create magic packet */ - if (strlen (quirk_str) != 4) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "unsupported jabra quirk format: '%s'", - quirk_str); - return FALSE; - } - rep = dfu_utils_buffer_parse_uint8 (quirk_str + 0); - adr = dfu_utils_buffer_parse_uint8 (quirk_str + 2); + rep = dfu_utils_buffer_parse_uint8 (priv->jabra_detach + 0); + adr = dfu_utils_buffer_parse_uint8 (priv->jabra_detach + 2); buf[0] = rep; buf[1] = adr; buf[2] = 0x00; @@ -1240,7 +1184,8 @@ } /* the device has no DFU runtime, so cheat */ - if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) + if (priv->state == DFU_STATE_APP_IDLE && + priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) return TRUE; /* ensure interface is claimed */ @@ -1289,6 +1234,7 @@ } /* success */ + priv->force_version = 0x0; fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_IDLE); return TRUE; } @@ -1323,7 +1269,8 @@ } /* the device has no DFU runtime, so cheat */ - if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + if (priv->state == DFU_STATE_APP_IDLE && + priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -1389,7 +1336,8 @@ } /* the device has no DFU runtime, so cheat */ - if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + if (priv->state == DFU_STATE_APP_IDLE && + priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -1458,7 +1406,8 @@ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* the device has no DFU runtime, so cheat */ - if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + if (priv->state == DFU_STATE_APP_IDLE && + priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { priv->state = DFU_STATE_APP_IDLE; priv->status = DFU_STATUS_OK; } @@ -1505,13 +1454,8 @@ dfu_device_probe (FuUsbDevice *device, GError **error) { DfuDevice *self = DFU_DEVICE (device); - DfuDevicePrivate *priv = GET_PRIVATE (self); GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); - /* set the quirks for this new device */ - priv->quirks = DFU_DEVICE_QUIRK_NONE; - dfu_device_apply_quirks (self); - /* add all the targets */ if (!dfu_device_add_targets (self, error)) { g_prefix_error (error, "%04x:%04x is not supported: ", @@ -1556,7 +1500,7 @@ g_autoptr(GUsbDevice) usb_device2 = NULL; /* close */ - fu_usb_device_close (FU_USB_DEVICE (device), NULL); + fu_device_close (FU_DEVICE (device), NULL); /* watch the device disappear and re-appear */ usb_device2 = g_usb_context_wait_for_replug (priv->usb_context, @@ -1569,7 +1513,7 @@ /* re-open with new device set */ fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_IDLE); fu_usb_device_set_dev (FU_USB_DEVICE (device), usb_device2); - if (!fu_usb_device_open (FU_USB_DEVICE (device), error)) + if (!fu_device_open (FU_DEVICE (device), error)) return FALSE; if (!dfu_device_refresh_and_clear (device, error)) return FALSE; @@ -1684,6 +1628,7 @@ } /* success */ + priv->force_version = 0x0; fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_IDLE); return TRUE; } @@ -2081,6 +2026,50 @@ return g_string_free (str, FALSE); } +static gboolean +dfu_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + DfuDevice *self = DFU_DEVICE (device); + DfuDevicePrivate *priv = GET_PRIVATE (self); + + if (g_strcmp0 (key, FU_QUIRKS_DFU_FLAGS) == 0) { + dfu_device_set_quirks_from_string (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_DFU_JABRA_DETACH) == 0) { + if (value != NULL && strlen (value) == 4) { + priv->jabra_detach = g_strdup (value); + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unsupported jabra quirk format"); + return FALSE; + } + if (g_strcmp0 (key, FU_QUIRKS_DFU_FORCE_VERSION) == 0) { + if (value != NULL && strlen (value) == 4) { + priv->force_version = dfu_utils_buffer_parse_uint16 (value); + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid DFU version"); + return FALSE; + } + + /* failed */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + /** * dfu_device_get_attributes_as_string: (skip) * @device: a #DfuDevice @@ -2112,3 +2101,75 @@ g_string_truncate (str, str->len - 1); return g_string_free (str, FALSE); } + +static void +dfu_device_finalize (GObject *object) +{ + DfuDevice *device = DFU_DEVICE (object); + DfuDevicePrivate *priv = GET_PRIVATE (device); + + if (priv->usb_context != NULL) + g_object_unref (priv->usb_context); + g_free (priv->chip_id); + g_free (priv->jabra_detach); + g_ptr_array_unref (priv->targets); + + G_OBJECT_CLASS (dfu_device_parent_class)->finalize (object); +} + +static void +dfu_device_class_init (DfuDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->set_quirk_kv = dfu_device_set_quirk_kv; + klass_usb_device->open = dfu_device_open; + klass_usb_device->close = dfu_device_close; + klass_usb_device->probe = dfu_device_probe; + + /** + * DfuDevice::status-changed: + * @device: the #DfuDevice instance that emitted the signal + * @status: the new #DfuStatus + * + * The ::status-changed signal is emitted when the status changes. + **/ + signals [SIGNAL_STATUS_CHANGED] = + g_signal_new ("status-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (DfuDeviceClass, status_changed), + NULL, NULL, g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + /** + * DfuDevice::state-changed: + * @device: the #DfuDevice instance that emitted the signal + * @state: the new #DfuState + * + * The ::state-changed signal is emitted when the state changes. + **/ + signals [SIGNAL_STATE_CHANGED] = + g_signal_new ("state-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (DfuDeviceClass, state_changed), + NULL, NULL, g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + object_class->finalize = dfu_device_finalize; +} + +static void +dfu_device_init (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + priv->iface_number = 0xff; + priv->runtime_pid = 0xffff; + priv->runtime_vid = 0xffff; + priv->runtime_release = 0xffff; + priv->state = DFU_STATE_APP_IDLE; + priv->status = DFU_STATUS_OK; + priv->targets = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + priv->timeout_ms = 1500; + priv->transfer_size = 64; +} diff -Nru fwupd-1.0.9/plugins/dfu/dfu-device.h fwupd-1.2.10/plugins/dfu/dfu-device.h --- fwupd-1.0.9/plugins/dfu/dfu-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_DEVICE_H -#define __DFU_DEVICE_H +#pragma once #include #include @@ -163,5 +161,3 @@ GUsbContext *dfu_device_get_usb_context (DfuDevice *device); G_END_DECLS - -#endif /* __DFU_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-device-private.h fwupd-1.2.10/plugins/dfu/dfu-device-private.h --- fwupd-1.0.9/plugins/dfu/dfu-device-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-device-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_DEVICE_PRIVATE_H -#define __DFU_DEVICE_PRIVATE_H +#pragma once #include #include @@ -27,5 +25,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_DEVICE_PRIVATE_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-element.c fwupd-1.2.10/plugins/dfu/dfu-element.c --- fwupd-1.0.9/plugins/dfu/dfu-element.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-element.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -229,7 +228,7 @@ g_assert (buf != NULL); memcpy (buf, data, length); - /* set the pading value */ + /* set the padding value */ if (priv->padding_value != 0x00) { memset (buf + length, priv->padding_value, diff -Nru fwupd-1.0.9/plugins/dfu/dfu-element.h fwupd-1.2.10/plugins/dfu/dfu-element.h --- fwupd-1.0.9/plugins/dfu/dfu-element.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-element.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_ELEMENT_H -#define __DFU_ELEMENT_H +#pragma once #include #include @@ -42,5 +40,3 @@ gchar *dfu_element_to_string (DfuElement *element); G_END_DECLS - -#endif /* __DFU_ELEMENT_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-firmware.c fwupd-1.2.10/plugins/dfu/dfu-firmware.c --- fwupd-1.0.9/plugins/dfu/dfu-firmware.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-firmware.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -22,7 +21,8 @@ #include #include -#include + +#include "fu-common-version.h" #include "dfu-common.h" #include "dfu-firmware.h" @@ -643,8 +643,8 @@ 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, + FWUPD_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 -Nru fwupd-1.0.9/plugins/dfu/dfu-firmware.h fwupd-1.2.10/plugins/dfu/dfu-firmware.h --- fwupd-1.0.9/plugins/dfu/dfu-firmware.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-firmware.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FIRMWARE_H -#define __DFU_FIRMWARE_H +#pragma once #include #include @@ -121,5 +119,3 @@ const gchar *key); G_END_DECLS - -#endif /* __DFU_FIRMWARE_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-dfu.c fwupd-1.2.10/plugins/dfu/dfu-format-dfu.c --- fwupd-1.0.9/plugins/dfu/dfu-format-dfu.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-dfu.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-dfu.h fwupd-1.2.10/plugins/dfu/dfu-format-dfu.h --- fwupd-1.0.9/plugins/dfu/dfu-format-dfu.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-dfu.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FORMAT_DFU_H -#define __DFU_FORMAT_DFU_H +#pragma once #include #include @@ -24,5 +22,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_FORMAT_DFU_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-dfuse.c fwupd-1.2.10/plugins/dfu/dfu-format-dfuse.c --- fwupd-1.0.9/plugins/dfu/dfu-format-dfuse.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-dfuse.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-dfuse.h fwupd-1.2.10/plugins/dfu/dfu-format-dfuse.h --- fwupd-1.0.9/plugins/dfu/dfu-format-dfuse.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-dfuse.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FORMAT_DFUSE_H -#define __DFU_FORMAT_DFUSE_H +#pragma once #include #include @@ -24,5 +22,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_FORMAT_DFUSE_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-ihex.c fwupd-1.2.10/plugins/dfu/dfu-format-ihex.c --- fwupd-1.0.9/plugins/dfu/dfu-format-ihex.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-ihex.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -34,19 +33,47 @@ 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 @@ -64,25 +91,19 @@ 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 = G_MAXUINT32; + guint32 seg_addr = 0x0; + g_auto(GStrv) lines = NULL; g_autoptr(DfuElement) element = NULL; g_autoptr(DfuImage) image = NULL; g_autoptr(GBytes) contents = NULL; - g_autoptr(GString) 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); @@ -92,95 +113,110 @@ 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 == G_MAXUINT32) + 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, @@ -188,19 +224,19 @@ (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: @@ -214,25 +250,28 @@ } 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: @@ -243,17 +282,9 @@ 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 */ @@ -266,19 +297,20 @@ } /* 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); + if (base_addr != G_MAXUINT32) + dfu_element_set_address (element, base_addr); dfu_image_add_element (image, element); dfu_firmware_add_image (firmware, image); dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); /* add optional signature */ - if (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); @@ -330,7 +362,7 @@ 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 -Nru fwupd-1.0.9/plugins/dfu/dfu-format-ihex.h fwupd-1.2.10/plugins/dfu/dfu-format-ihex.h --- fwupd-1.0.9/plugins/dfu/dfu-format-ihex.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-ihex.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FORMAT_IHEX_H -#define __DFU_FORMAT_IHEX_H +#pragma once #include #include @@ -24,5 +22,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_FORMAT_IHEX_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-metadata.c fwupd-1.2.10/plugins/dfu/dfu-format-metadata.c --- fwupd-1.0.9/plugins/dfu/dfu-format-metadata.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-metadata.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -162,7 +161,7 @@ g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "metdata key too long: %s", + "metadata key too long: %s", key); return NULL; } diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-metadata.h fwupd-1.2.10/plugins/dfu/dfu-format-metadata.h --- fwupd-1.0.9/plugins/dfu/dfu-format-metadata.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-metadata.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FORMAT_METADATA_H -#define __DFU_FORMAT_METADATA_H +#pragma once #include #include @@ -23,5 +21,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_FORMAT_METADATA_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-raw.c fwupd-1.2.10/plugins/dfu/dfu-format-raw.c --- fwupd-1.0.9/plugins/dfu/dfu-format-raw.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-raw.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-raw.h fwupd-1.2.10/plugins/dfu/dfu-format-raw.h --- fwupd-1.0.9/plugins/dfu/dfu-format-raw.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-raw.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FORMAT_RAW_H -#define __DFU_FORMAT_RAW_H +#pragma once #include #include @@ -24,5 +22,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_FORMAT_RAW_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-format-srec.c fwupd-1.2.10/plugins/dfu/dfu-format-srec.c --- fwupd-1.0.9/plugins/dfu/dfu-format-srec.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-srec.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -39,15 +38,6 @@ 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 @@ -66,65 +56,67 @@ 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; } @@ -133,28 +125,24 @@ 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, @@ -162,6 +150,63 @@ "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, @@ -170,76 +215,34 @@ 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, @@ -261,23 +264,36 @@ 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)); + guint bytecnt = 0; + guint32 len_hole = rec_addr32 - addr32_last; + + /* fill any holes, but only up to 1Mb to avoid a DoS */ + if (addr32_last > 0 && len_hole > 0x100000) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "hole of 0x%x bytes too large to fill", + (guint) len_hole); + return FALSE; + } + if (addr32_last > 0x0 && len_hole > 1) { + g_debug ("filling address 0x%08x to 0x%08x", + addr32_last + 1, addr32_last + len_hole - 1); + for (guint j = 0; j < len_hole; j++) + g_string_append_c (outbuf, 0xff); + } + + /* add data */ + for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { + guint8 tmp = dfu_utils_buffer_parse_uint8 (line + i); g_string_append_c (outbuf, tmp); + bytecnt++; } if (element_address == 0x0) element_address = rec_addr32; + addr32_last = rec_addr32 + bytecnt; } - 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 -Nru fwupd-1.0.9/plugins/dfu/dfu-format-srec.h fwupd-1.2.10/plugins/dfu/dfu-format-srec.h --- fwupd-1.0.9/plugins/dfu/dfu-format-srec.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-format-srec.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_FORMAT_SREC_H -#define __DFU_FORMAT_SREC_H +#pragma once #include #include @@ -29,5 +27,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_FORMAT_SREC_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-image.c fwupd-1.2.10/plugins/dfu/dfu-image.c --- fwupd-1.0.9/plugins/dfu/dfu-image.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-image.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-image.h fwupd-1.2.10/plugins/dfu/dfu-image.h --- fwupd-1.0.9/plugins/dfu/dfu-image.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-image.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_IMAGE_H -#define __DFU_IMAGE_H +#pragma once #include #include @@ -44,5 +42,3 @@ gchar *dfu_image_to_string (DfuImage *image); G_END_DECLS - -#endif /* __DFU_IMAGE_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-patch.c fwupd-1.2.10/plugins/dfu/dfu-patch.c --- fwupd-1.0.9/plugins/dfu/dfu-patch.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-patch.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-patch.h fwupd-1.2.10/plugins/dfu/dfu-patch.h --- fwupd-1.0.9/plugins/dfu/dfu-patch.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-patch.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_PATCH_H -#define __DFU_PATCH_H +#pragma once #include #include @@ -55,5 +53,3 @@ GBytes *dfu_patch_get_checksum_new (DfuPatch *self); G_END_DECLS - -#endif /* __DFU_PATCH_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu.quirk fwupd-1.2.10/plugins/dfu/dfu.quirk --- fwupd-1.0.9/plugins/dfu/dfu.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -1,235 +1,342 @@ -[fwupd-dfu] +# 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 -USB\VID_0A5C&PID_6412=ignore-runtime +[DeviceInstanceId=USB\VID_0A5C&PID_6412] +Plugin = dfu +DfuFlags = ignore-runtime # Openmoko Freerunner / GTA02 -USB\VID_1D50&PID_5119=ignore-polltimeout|no-pid-change|no-dfu-runtime|action-required|no-get-status-upload +[DeviceInstanceId=USB\VID_1D50&PID_5119] +Plugin = dfu +DfuFlags = ignore-polltimeout,no-pid-change,no-dfu-runtime,action-required,no-get-status-upload # OpenPCD Reader -USB\VID_16C0&PID_076B=ignore-polltimeout +[DeviceInstanceId=USB\VID_16C0&PID_076B] +Plugin = dfu +DfuFlags = ignore-polltimeout # SIMtrace -USB\VID_16C0&PID_0762=ignore-polltimeout +[DeviceInstanceId=USB\VID_16C0&PID_0762] +Plugin = dfu +DfuFlags = ignore-polltimeout # OpenPICC -USB\VID_16C0&PID_076C=ignore-polltimeout +[DeviceInstanceId=USB\VID_16C0&PID_076C] +Plugin = dfu +DfuFlags = ignore-polltimeout # Siemens AG, PXM 40 & PXM 50 -USB\VID_0908&PID_02C4&REV_0000=ignore-polltimeout -USB\VID_0908&PID_02C5&REV_0000=ignore-polltimeout +[DeviceInstanceId=USB\VID_0908&PID_02C4] +Plugin = dfu +[DeviceInstanceId=USB\VID_0908&PID_02C5] +Plugin = dfu +[DeviceInstanceId=USB\VID_0908&PID_02C4&REV_0000] +DfuFlags = ignore-polltimeout +[DeviceInstanceId=USB\VID_0908&PID_02C5&REV_0000] +DfuFlags = ignore-polltimeout # Midiman M-Audio Transit -USB\VID_0763&PID_2806=ignore-polltimeout +[DeviceInstanceId=USB\VID_0763&PID_2806] +Plugin = dfu +DfuFlags = ignore-polltimeout # LPC DFU bootloader -USB\VID_1FC9&PID_000C=force-dfu-mode +[DeviceInstanceId=USB\VID_1FC9&PID_000C] +Plugin = dfu +DfuFlags = force-dfu-mode # m-stack DFU -USB\VID_273F&PID_1003=attach-upload-download -USB\VID_273F&PID_100A=attach-upload-download +[DeviceInstanceId=USB\VID_273F&PID_1003] +DfuFlags = attach-upload-download +[DeviceInstanceId=USB\VID_273F&PID_100A] +DfuFlags = attach-upload-download # HydraBus -USB\VID_1D50&PID_60A7=no-dfu-runtime|action-required - -# Jabra 410, 510, 710 and 810 -USB\VID_0B0E&PID_0412=no-dfu-runtime -USB\VID_0B0E&PID_0420=no-dfu-runtime -USB\VID_0B0E&PID_2475=no-dfu-runtime -USB\VID_0B0E&PID_2456=no-dfu-runtime - -# Jabra 410, 510, 710 and 810 (DFU mode) -USB\VID_0B0E&PID_0411=no-pid-change|force-dfu-mode|ignore-upload|attach-extra-reset -USB\VID_0B0E&PID_0421=no-pid-change|force-dfu-mode|ignore-upload|attach-extra-reset -USB\VID_0B0E&PID_0982=no-pid-change|force-dfu-mode|ignore-upload|attach-extra-reset -USB\VID_0B0E&PID_0971=no-pid-change|force-dfu-mode|ignore-upload|attach-extra-reset +[DeviceInstanceId=USB\VID_1D50&PID_60A7] +Plugin = dfu +DfuFlags = no-dfu-runtime,action-required + +# Jabra 410 +[DeviceInstanceId=USB\VID_0B0E&PID_0412] +Plugin = dfu +DfuFlags = no-dfu-runtime +DfuJabraDetach = 0201 +CounterpartGuid = USB\VID_0B0E&PID_0411 + +# Jabra 510 +[DeviceInstanceId=USB\VID_0B0E&PID_0420] +Plugin = dfu +DfuFlags = no-dfu-runtime +DfuJabraDetach = 0201 +CounterpartGuid = USB\VID_0B0E&PID_0421 + +# Jabra 710 +[DeviceInstanceId=USB\VID_0B0E&PID_2475] +Plugin = dfu +DfuFlags = no-dfu-runtime +DfuJabraDetach = 0508 +CounterpartGuid = USB\VID_0B0E&PID_0982 + +# Jabra 810 +[DeviceInstanceId=USB\VID_0B0E&PID_2456] +Plugin = dfu +DfuFlags = no-dfu-runtime +DfuJabraDetach = 0508 +CounterpartGuid = USB\VID_0B0E&PID_0971 + +[DeviceInstanceId=USB\VID_0B0E&PID_0411] +Plugin = dfu +DfuFlags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset + +[DeviceInstanceId=USB\VID_0B0E&PID_0421] +Plugin = dfu +DfuFlags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset + +[DeviceInstanceId=USB\VID_0B0E&PID_0982] +Plugin = dfu +DfuFlags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset + +[DeviceInstanceId=USB\VID_0B0E&PID_0971] +Plugin = dfu +DfuFlags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset # Atmel AT90USB Bootloader -USB\VID_03EB&PID_2FF7=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FF9=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FFA=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FFB=use-any-interface|legacy-protocol|force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FF7] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FF9] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FFA] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FFB] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode # Atmel ATMEGA Bootloader -USB\VID_03EB&PID_2FEE=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FEF=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FF0=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FF2=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FF3=use-any-interface|legacy-protocol|force-dfu-mode -USB\VID_03EB&PID_2FF4=use-any-interface|legacy-protocol|force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FEE] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FEF] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FF0] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FF2] +Plugin = dfu +DfuFlags = use-any-interface,legacy-protocol,force-dfu-mode +[DeviceInstanceId=USB\VID_03EB&PID_2FF3] +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 -USB\VID_03EB&PID_2FE2=use-any-interface|force-dfu-mode - -[fwupd-dfu-force-version] +[DeviceInstanceId=USB\VID_03EB&PID_2FE2] +Plugin = dfu +DfuFlags = use-any-interface,force-dfu-mode # Leaflabs Maple3 -USB\VID_1EAF&PID_0003&REV_0200=0110 +[DeviceInstanceId=USB\VID_1EAF&PID_0003&REV_0200] +Plugin = dfu +DfuForceVersion = 0110 # Atmel FLIP Bootloader -USB\VID_03EB=ff01 - -[fwupd-dfu-jabra-detach] - -# Jabra 410 and 510 -USB\VID_0B0E&PID_0412=0201 -USB\VID_0B0E&PID_0420=0201 - -# Jabra 710 and 810 -USB\VID_0B0E&PID_2475=0508 -USB\VID_0B0E&PID_2456=0508 - -[FuUsbDevice:guid] - -# Jabra 410, 510, 710 and 810 -USB\VID_0B0E&PID_0412=USB\VID_0B0E&PID_0411 -USB\VID_0B0E&PID_0420=USB\VID_0B0E&PID_0421 -USB\VID_0B0E&PID_2475=USB\VID_0B0E&PID_0982 -USB\VID_0B0E&PID_2456=USB\VID_0B0E&PID_0971 - -[fwupd-dfu-avr-chip-id] +[DeviceInstanceId=USB\VID_03EB] +Plugin = dfu +DfuForceVersion = ff01 # AT32UC3B1256 [BLDR][USER] USER@0x2000, BLDR+USER=0x40000 -0x58200203=@Flash/0x2000/1*248Kg +[AvrChipId=0x58200203] +DfuAltName = @Flash/0x2000/1*248Kg # AT32UC3A3256 [BLDR][USER] USER@0x2000, BLDR+USER=0x40000 -0x58200204=@Flash/0x2000/1*248Kg +[AvrChipId=0x58200204] +DfuAltName = @Flash/0x2000/1*248Kg # AT90USB1287 [USER][BLDR] BLDR@0x1e000, BLDR+USER=0x20000 -0x581e9782=@Flash/0x0/1*120Kg +[AvrChipId=0x581e9782] +DfuAltName = @Flash/0x0/1*120Kg # AT90USB647 [USER][BLDR] BLDR@0x0e000, BLDR+USER=0x10000 -0x581e9682=@Flash/0x0/1*56Kg - # AT90USB646 [USER][BLDR] BLDR@0x0e000, BLDR+USER=0x10000 -0x581e9682=@Flash/0x0/1*56Kg +[AvrChipId=0x581e9682] +DfuAltName = @Flash/0x0/1*56Kg # ATmega32U4 [USER][BLDR] BLDR@0x07000, BLDR+USER=0x08000 -0x581e9587=@Flash/0x0/1*28Kg +[AvrChipId=0x581e9587] +DfuAltName = @Flash/0x0/1*28Kg # ATmega16U4 [USER][BLDR] BLDR@0x03000, BLDR+USER=0x04000 -0x581e9488=@Flash/0x0/1*12Kg +[AvrChipId=0x581e9488] +DfuAltName = @Flash/0x0/1*12Kg # ATmega32U2 [USER][BLDR] BLDR@0x07000, BLDR+USER=0x08000 -0x581e958a=@Flash/0x0/1*28Kg +[AvrChipId=0x581e958a] +DfuAltName = @Flash/0x0/1*28Kg # ATmega16U2 [USER][BLDR] BLDR@0x03000, BLDR+USER=0x04000 -0x581e9489=@Flash/0x0/1*12Kg +[AvrChipId=0x581e9489] +DfuAltName = @Flash/0x0/1*12Kg # AT90USB162 [USER][BLDR] BLDR@0x03000, BLDR+USER=0x04000 -0x581e9482=@Flash/0x0/1*12Kg +[AvrChipId=0x581e9482] +DfuAltName = @Flash/0x0/1*12Kg # ATmega8U2 [USER][BLDR] BLDR@0x01000, BLDR+USER=0x02000 -0x581e9389=@Flash/0x0/1*4Kg +[AvrChipId=0x581e9389] +DfuAltName = @Flash/0x0/1*4Kg # AT90USB82 [USER][BLDR] BLDR@0x01000, BLDR+USER=0x02000 -0x581e9382=@Flash/0x0/1*4Kg +[AvrChipId=0x581e9382] +DfuAltName = @Flash/0x0/1*4Kg # ATxmega16A4 [USER] USER=0x4000 -0x1e9441=@Flash/0x0/1*16Kg +[AvrChipId=0x1e9441] +DfuAltName = @Flash/0x0/1*16Kg # ATxmega16C4 [USER] USER=0x4000 -0x1e9544=@Flash/0x0/1*16Kg +[AvrChipId=0x1e9544] +DfuAltName = @Flash/0x0/1*16Kg # ATxmega16D4 [USER] USER=0x4000 -0x1e9442=@Flash/0x0/1*16Kg +[AvrChipId=0x1e9442] +DfuAltName = @Flash/0x0/1*16Kg # ATxmega32A4 [USER] USER=0x8000 -0x1e9541=@Flash/0x0/1*32Kg +[AvrChipId=0x1e9541] +DfuAltName = @Flash/0x0/1*32Kg # ATxmega32C4 [USER] USER=0x8000 -0x1e9443=@Flash/0x0/1*32Kg +[AvrChipId=0x1e9443] +DfuAltName = @Flash/0x0/1*32Kg # ATxmega32D4 [USER] USER=0x8000 -0x1e9542=@Flash/0x0/1*32Kg +[AvrChipId=0x1e9542] +DfuAltName = @Flash/0x0/1*32Kg # ATxmega64A4 [USER] USER=0x10000 -0x1e9646=@Flash/0x0/1*64Kg +[AvrChipId=0x1e9646] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega64C3 [USER] USER=0x10000 -0x1e9649=@Flash/0x0/1*64Kg +[AvrChipId=0x1e9649] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega64D3 [USER] USER=0x10000 -0x1e964a=@Flash/0x0/1*64Kg +[AvrChipId=0x1e964a] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega64D4 [USER] USER=0x10000 -0x1e9647=@Flash/0x0/1*64Kg +[AvrChipId=0x1e9647] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega64A1 [USER] USER=0x10000 -0x1e964e=@Flash/0x0/1*64Kg +[AvrChipId=0x1e964e] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega64A3 [USER] USER=0x10000 -0x1e9642=@Flash/0x0/1*64Kg +[AvrChipId=0x1e9642] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega64B1 [USER] USER=0x10000 -0x1e9652=@Flash/0x0/1*64Kg +[AvrChipId=0x1e9652] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega64B3 [USER] USER=0x10000 -0x1e9651=@Flash/0x0/1*64Kg +[AvrChipId=0x1e9651] +DfuAltName = @Flash/0x0/1*64Kg # ATxmega128C3 [USER] USER=0x20000 -0x1e9752=@Flash/0x0/1*128Kg +[AvrChipId=0x1e9752] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128D3 [USER] USER=0x20000 -0x1e9748=@Flash/0x0/1*128Kg +[AvrChipId=0x1e9748] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128D4 [USER] USER=0x20000 -0x1e9747=@Flash/0x0/1*128Kg + [AvrChipId=0x1e9747] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128A1 [USER] USER=0x20000 -0x1e974c=@Flash/0x0/1*128Kg +[AvrChipId=0x1e974c] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128A1D [USER] USER=0x20000 -0x1e9741=@Flash/0x0/1*128Kg +[AvrChipId=0x1e9741] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128A3 [USER] USER=0x20000 -0x1e9742=@Flash/0x0/1*128Kg +[AvrChipId=0x1e9742] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128A4 [USER] USER=0x20000 -0x1e9746=@Flash/0x0/1*128Kg +[AvrChipId=0x1e9746] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128B1 [USER] USER=0x20000 -0x1e974d=@Flash/0x0/1*128Kg +[AvrChipId=0x1e974d] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega128B3 [USER] USER=0x20000 -0x1e974b=@Flash/0x0/1*128Kg +[AvrChipId=0x1e974b] +DfuAltName = @Flash/0x0/1*128Kg # ATxmega192C3 [USER] USER=0x30000 -0x1e9751=@Flash/0x0/1*192Kg +[AvrChipId=0x1e9751] +DfuAltName = @Flash/0x0/1*192Kg # ATxmega192D3 [USER] USER=0x30000 -0x1e9749=@Flash/0x0/1*192Kg +[AvrChipId=0x1e9749] +DfuAltName = @Flash/0x0/1*192Kg # ATxmega192A1 [USER] USER=0x30000 -0x1e974e=@Flash/0x0/1*192Kg +[AvrChipId=0x1e974e] +DfuAltName = @Flash/0x0/1*192Kg # ATxmega192A3 [USER] USER=0x30000 -0x1e9744=@Flash/0x0/1*192Kg +[AvrChipId=0x1e9744] +DfuAltName = @Flash/0x0/1*192Kg # ATxmega256 [USER] USER=0x40000 -0x1e9846=@Flash/0x0/1*256Kg +[AvrChipId=0x1e9846] +DfuAltName = @Flash/0x0/1*256Kg # ATxmega256D3 [USER] USER=0x40000 -0x1e9844=@Flash/0x0/1*256Kg +[AvrChipId=0x1e9844] +DfuAltName = @Flash/0x0/1*256Kg # ATxmega256A3 [USER] USER=0x40000 -0x1e9842=@Flash/0x0/1*256Kg +[AvrChipId=0x1e9842] +DfuAltName = @Flash/0x0/1*256Kg # ATxmega256A3B [USER] USER=0x40000 -0x1e9843=@Flash/0x0/1*256Kg +[AvrChipId=0x1e9843] +DfuAltName = @Flash/0x0/1*256Kg # ATxmega384C3 [USER] USER=0x60000 -0x1e9845=@Flash/0x0/1*384Kg +[AvrChipId=0x1e9845] +DfuAltName = @Flash/0x0/1*384Kg # ATxmega384D3 [USER] USER=0x60000 -0x1e9847=@Flash/0x0/1*384Kg +[AvrChipId=0x1e9847] +DfuAltName = @Flash/0x0/1*384Kg # ATxmega8E5 [USER] USER=0x2000 -0x1e9341=@Flash/0x0/1*8Kg +[AvrChipId=0x1e9341] +DfuAltName = @Flash/0x0/1*8Kg # ATxmega16E5 [USER] USER=0x4000 -0x1e9445=@Flash/0x0/1*16Kg +[AvrChipId=0x1e9445] +DfuAltName = @Flash/0x0/1*16Kg # ATxmega32E5 [USER] USER=0x8000 -0x1e954c=@Flash/0x0/1*32Kg +[AvrChipId=0x1e954c] +DfuAltName = @Flash/0x0/1*32Kg diff -Nru fwupd-1.0.9/plugins/dfu/dfu-sector.c fwupd-1.2.10/plugins/dfu/dfu-sector.c --- fwupd-1.0.9/plugins/dfu/dfu-sector.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-sector.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-sector.h fwupd-1.2.10/plugins/dfu/dfu-sector.h --- fwupd-1.0.9/plugins/dfu/dfu-sector.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-sector.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_SECTOR_H -#define __DFU_SECTOR_H +#pragma once #include #include @@ -50,5 +48,3 @@ gchar *dfu_sector_to_string (DfuSector *sector); G_END_DECLS - -#endif /* __DFU_SECTOR_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-sector-private.h fwupd-1.2.10/plugins/dfu/dfu-sector-private.h --- fwupd-1.0.9/plugins/dfu/dfu-sector-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-sector-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_SECTOR_PRIVATE_H -#define __DFU_SECTOR_PRIVATE_H +#pragma once #include "dfu-sector.h" @@ -20,5 +18,3 @@ DfuSectorCap cap); G_END_DECLS - -#endif /* __DFU_SECTOR_PRIVATE_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-self-test.c fwupd-1.2.10/plugins/dfu/dfu-self-test.c --- fwupd-1.0.9/plugins/dfu/dfu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -11,7 +10,6 @@ #include #include -#include "dfu-chunked.h" #include "dfu-cipher-xtea.h" #include "dfu-common.h" #include "dfu-device-private.h" @@ -37,34 +35,6 @@ return g_strdup (full_tmp); } -static gchar * -_g_bytes_compare_verbose (GBytes *bytes1, GBytes *bytes2) -{ - const guint8 *data1; - const guint8 *data2; - gsize length1; - gsize length2; - - data1 = g_bytes_get_data (bytes1, &length1); - data2 = g_bytes_get_data (bytes2, &length2); - - /* not the same length */ - if (length1 != length2) { - return g_strdup_printf ("got %" G_GSIZE_FORMAT " bytes, " - "expected %" G_GSIZE_FORMAT, - length1, length2); - } - - /* return 00 01 02 03 */ - for (guint i = 0; i < length1; i++) { - if (data1[i] != data2[i]) { - return g_strdup_printf ("got 0x%02x, expected 0x%02x @ 0x%04x", - data1[i], data2[i], i); - } - } - return NULL; -} - static void dfu_cipher_xtea_func (void) { @@ -182,7 +152,9 @@ roundtrip = dfu_firmware_write_data (firmware, &error); g_assert_no_error (error); g_assert (roundtrip != NULL); - g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, fw), ==, NULL); + ret = fu_common_bytes_compare (roundtrip, fw, &error); + g_assert_no_error (error); + g_assert_true (ret); } static void @@ -250,14 +222,16 @@ g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 0x8eB4); g_assert_cmpint (dfu_firmware_get_cipher_kind (firmware), ==, DFU_CIPHER_KIND_NONE); - /* can we roundtrip without loosing data */ + /* can we roundtrip without losing data */ roundtrip_orig = dfu_self_test_get_bytes_for_file (file, &error); g_assert_no_error (error); g_assert (roundtrip_orig != NULL); roundtrip = dfu_firmware_write_data (firmware, &error); g_assert_no_error (error); g_assert (roundtrip != NULL); - g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, roundtrip_orig), ==, NULL); + ret = fu_common_bytes_compare (roundtrip, roundtrip_orig, &error); + g_assert_no_error (error); + g_assert_true (ret); } static void @@ -289,7 +263,7 @@ g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 0x168d5); g_assert_cmpint (dfu_firmware_get_cipher_kind (firmware), ==, DFU_CIPHER_KIND_NONE); - /* can we roundtrip without loosing data */ + /* can we roundtrip without losing data */ roundtrip_orig = dfu_self_test_get_bytes_for_file (file, &error); g_assert_no_error (error); g_assert (roundtrip_orig != NULL); @@ -301,7 +275,9 @@ // g_bytes_get_data (roundtrip, NULL), // g_bytes_get_size (roundtrip), NULL); - g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, roundtrip_orig), ==, NULL); + ret = fu_common_bytes_compare (roundtrip, roundtrip_orig, &error); + g_assert_no_error (error); + g_assert_true (ret); /* use usual image name copying */ g_unsetenv ("DFU_SELF_TEST_IMAGE_MEMCPY_NAME"); @@ -332,15 +308,16 @@ g_assert_cmpstr (dfu_firmware_get_metadata (firmware, "key"), ==, "value"); g_assert_cmpstr (dfu_firmware_get_metadata (firmware, "???"), ==, NULL); - /* can we roundtrip without loosing data */ + /* can we roundtrip without losing data */ roundtrip_orig = dfu_self_test_get_bytes_for_file (file, &error); g_assert_no_error (error); g_assert (roundtrip_orig != NULL); roundtrip = dfu_firmware_write_data (firmware, &error); g_assert_no_error (error); g_assert (roundtrip != NULL); - - g_assert_cmpstr (_g_bytes_compare_verbose (roundtrip, roundtrip_orig), ==, NULL); + ret = fu_common_bytes_compare (roundtrip, roundtrip_orig, &error); + g_assert_no_error (error); + g_assert_true (ret); } static void @@ -429,7 +406,9 @@ data_ref = dfu_self_test_get_bytes_for_file (file_bin, &error); g_assert_no_error (error); g_assert (data_ref != NULL); - g_assert_cmpstr (_g_bytes_compare_verbose (data_bin, data_ref), ==, NULL); + ret = fu_common_bytes_compare (data_bin, data_ref, &error); + g_assert_no_error (error); + g_assert_true (ret); } static void @@ -473,7 +452,9 @@ data_ref = dfu_self_test_get_bytes_for_file (file_bin, &error); g_assert_no_error (error); g_assert (data_ref != NULL); - g_assert_cmpstr (_g_bytes_compare_verbose (data_bin, data_ref), ==, NULL); + ret = fu_common_bytes_compare (data_bin, data_ref, &error); + g_assert_no_error (error); + g_assert_true (ret); /* export a ihex file (which will be slightly different due to * non-continous regions being expanded */ @@ -500,7 +481,9 @@ data_bin2 = dfu_firmware_write_data (firmware, &error); g_assert_no_error (error); g_assert (data_bin2 != NULL); - g_assert_cmpstr (_g_bytes_compare_verbose (data_bin, data_bin2), ==, NULL); + ret = fu_common_bytes_compare (data_bin, data_bin2, &error); + g_assert_no_error (error); + g_assert_true (ret); } static void @@ -799,50 +782,6 @@ g_debug ("serialized blob %s", serialized_str); } -static void -dfu_chunked_func (void) -{ - g_autofree gchar *chunked1_str = NULL; - g_autofree gchar *chunked2_str = NULL; - g_autofree gchar *chunked3_str = NULL; - g_autofree gchar *chunked4_str = NULL; - g_autoptr(GPtrArray) chunked1 = NULL; - g_autoptr(GPtrArray) chunked2 = NULL; - g_autoptr(GPtrArray) chunked3 = NULL; - g_autoptr(GPtrArray) chunked4 = NULL; - - chunked3 = dfu_chunked_new ((const guint8 *) "123456", 6, 0x0, 3, 3); - chunked3_str = dfu_chunked_to_string (chunked3); - g_print ("\n%s", chunked3_str); - g_assert_cmpstr (chunked3_str, ==, "#00: page:00 addr:0000 len:03 123\n" - "#01: page:01 addr:0000 len:03 456\n"); - - chunked4 = dfu_chunked_new ((const guint8 *) "123456", 6, 0x4, 4, 4); - chunked4_str = dfu_chunked_to_string (chunked4); - g_print ("\n%s", chunked4_str); - g_assert_cmpstr (chunked4_str, ==, "#00: page:01 addr:0000 len:04 1234\n" - "#01: page:02 addr:0000 len:02 56\n"); - - chunked1 = dfu_chunked_new ((const guint8 *) "0123456789abcdef", 16, 0x0, 10, 4); - chunked1_str = dfu_chunked_to_string (chunked1); - g_print ("\n%s", chunked1_str); - g_assert_cmpstr (chunked1_str, ==, "#00: page:00 addr:0000 len:04 0123\n" - "#01: page:00 addr:0004 len:04 4567\n" - "#02: page:00 addr:0008 len:02 89\n" - "#03: page:01 addr:0000 len:04 abcd\n" - "#04: page:01 addr:0004 len:02 ef\n"); - - chunked2 = dfu_chunked_new ((const guint8 *) "XXXXXXYYYYYYZZZZZZ", 18, 0x0, 6, 4); - chunked2_str = dfu_chunked_to_string (chunked2); - g_print ("\n%s", chunked2_str); - g_assert_cmpstr (chunked2_str, ==, "#00: page:00 addr:0000 len:04 XXXX\n" - "#01: page:00 addr:0004 len:02 XX\n" - "#02: page:01 addr:0000 len:04 YYYY\n" - "#03: page:01 addr:0004 len:02 YY\n" - "#04: page:02 addr:0000 len:04 ZZZZ\n" - "#05: page:02 addr:0004 len:02 ZZ\n"); -} - int main (int argc, char **argv) { @@ -856,7 +795,6 @@ /* tests go here */ g_test_add_func ("/dfu/firmware{srec}", dfu_firmware_srec_func); - g_test_add_func ("/dfu/chunked", dfu_chunked_func); g_test_add_func ("/dfu/patch", dfu_patch_func); g_test_add_func ("/dfu/patch{merges}", dfu_patch_merges_func); g_test_add_func ("/dfu/patch{apply}", dfu_patch_apply_func); diff -Nru fwupd-1.0.9/plugins/dfu/dfu-target-avr.c fwupd-1.2.10/plugins/dfu/dfu-target-avr.c --- fwupd-1.0.9/plugins/dfu/dfu-target-avr.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-target-avr.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -10,7 +9,8 @@ #include #include -#include "dfu-chunked.h" +#include "fu-chunk.h" + #include "dfu-common.h" #include "dfu-sector.h" #include "dfu-target-avr.h" @@ -19,6 +19,22 @@ #include "fwupd-error.h" +/** + * FU_QUIRKS_DFU_AVR_ALT_NAME: + * @key: the AVR chip ID, e.g. `0x58200204` + * @value: the UM0424 sector description, e.g. `@Flash/0x2000/1*248Kg` + * + * Assigns a sector description for the chip ID. This is required so fwupd can + * program the user firmware avoiding the bootloader and for checking the total + * element size. + * + * The chip ID can be found from a datasheet or using `dfu-tool list` when the + * hardware is connected and in bootloader mode. + * + * Since: 1.0.1 + */ +#define FU_QUIRKS_DFU_AVR_ALT_NAME "DfuAltName" + typedef struct { guint32 device_id; } DfuTargetAvrPrivate; @@ -400,6 +416,7 @@ gsize sz; guint32 device_id_be; g_autofree gchar *chip_id = NULL; + g_autofree gchar *chip_id_prefixed = NULL; g_autoptr(GBytes) chunk_sig = NULL; /* already done */ @@ -414,8 +431,10 @@ return FALSE; } else { chunk_sig = dfu_target_avr32_get_chip_signature (target, error); - if (chunk_sig == NULL) + if (chunk_sig == NULL) { + g_prefix_error (error, "failed to get chip signature: "); return FALSE; + } } /* get data back */ @@ -449,9 +468,10 @@ /* set the alt-name using the device ID */ dfu_device_set_chip_id (dfu_target_get_device (target), chip_id); device = dfu_target_get_device (target); + chip_id_prefixed = g_strdup_printf ("AvrChipId=%s", chip_id); quirk_str = fu_quirks_lookup_by_id (fu_device_get_quirks (FU_DEVICE (device)), - FU_QUIRKS_DFU_AVR_CHIP_ID, - chip_id); + chip_id_prefixed, + FU_QUIRKS_DFU_AVR_ALT_NAME); if (quirk_str == NULL) { dfu_device_remove_attribute (dfu_target_get_device (target), DFU_DEVICE_ATTRIBUTE_CAN_DOWNLOAD); @@ -482,7 +502,7 @@ guint16 page_last = G_MAXUINT16; guint32 address; guint32 address_offset = 0x0; - g_autoptr(GPtrArray) packets = NULL; + g_autoptr(GPtrArray) chunks = NULL; const guint8 footer[] = { 0x00, 0x00, 0x00, 0x00, /* CRC */ 16, /* len */ 'D', 'F', 'U', /* signature */ @@ -535,56 +555,56 @@ /* chunk up the memory space into pages */ data = g_bytes_get_data (blob, NULL); - packets = dfu_chunked_new (data + address_offset, - g_bytes_get_size (blob) - address_offset, - dfu_sector_get_address (sector), - ATMEL_64KB_PAGE, - ATMEL_MAX_TRANSFER_SIZE); + chunks = fu_chunk_array_new (data + address_offset, + g_bytes_get_size (blob) - address_offset, + dfu_sector_get_address (sector), + ATMEL_64KB_PAGE, + ATMEL_MAX_TRANSFER_SIZE); /* update UI */ dfu_target_set_action (target, FWUPD_STATUS_DEVICE_WRITE); /* process each chunk */ - for (guint i = 0; i < packets->len; i++) { - const DfuChunkedPacket *packet = g_ptr_array_index (packets, i); + for (guint i = 0; i < chunks->len; i++) { + const FuChunk *chk = g_ptr_array_index (chunks, i); g_autofree guint8 *buf = NULL; g_autoptr(GBytes) chunk_tmp = NULL; /* select page if required */ - if (packet->page != page_last) { + if (chk->page != page_last) { if (dfu_device_has_quirk (dfu_target_get_device (target), DFU_DEVICE_QUIRK_LEGACY_PROTOCOL)) { if (!dfu_target_avr_select_memory_page (target, - packet->page, + chk->page, error)) return FALSE; } else { if (!dfu_target_avr32_select_memory_page (target, - packet->page, + chk->page, error)) return FALSE; } - page_last = packet->page; + page_last = chk->page; } - /* create packet with header and footer */ - buf = g_malloc0 (packet->data_sz + header_sz + sizeof(footer)); + /* create chk with header and footer */ + buf = g_malloc0 (chk->data_sz + header_sz + sizeof(footer)); buf[0] = DFU_AVR32_GROUP_DOWNLOAD; buf[1] = DFU_AVR32_CMD_PROGRAM_START; - fu_common_write_uint16 (&buf[2], packet->address, G_BIG_ENDIAN); - fu_common_write_uint16 (&buf[4], packet->address + packet->data_sz - 1, G_BIG_ENDIAN); - memcpy (&buf[header_sz], packet->data, packet->data_sz); - memcpy (&buf[header_sz + packet->data_sz], footer, sizeof(footer)); + fu_common_write_uint16 (&buf[2], chk->address, G_BIG_ENDIAN); + fu_common_write_uint16 (&buf[4], chk->address + chk->data_sz - 1, G_BIG_ENDIAN); + memcpy (&buf[header_sz], chk->data, chk->data_sz); + memcpy (&buf[header_sz + chk->data_sz], footer, sizeof(footer)); /* download data */ - chunk_tmp = g_bytes_new_static (buf, packet->data_sz + header_sz + sizeof(footer)); + chunk_tmp = g_bytes_new_static (buf, chk->data_sz + header_sz + sizeof(footer)); g_debug ("sending %" G_GSIZE_FORMAT " bytes to the hardware", g_bytes_get_size (chunk_tmp)); if (!dfu_target_download_chunk (target, i, chunk_tmp, error)) return FALSE; /* update UI */ - dfu_target_set_percentage (target, i + 1, packets->len); + dfu_target_set_percentage (target, i + 1, chunks->len); } /* done */ @@ -605,7 +625,7 @@ g_autoptr(DfuElement) element = NULL; g_autoptr(GBytes) contents = NULL; g_autoptr(GBytes) contents_truncated = NULL; - g_autoptr(GPtrArray) packets = NULL; + g_autoptr(GPtrArray) blobs = NULL; g_autoptr(GPtrArray) chunks = NULL; DfuSector *sector; @@ -636,63 +656,63 @@ address &= ~0x80000000; /* chunk up the memory space into pages */ - packets = dfu_chunked_new (NULL, maximum_size, address, - ATMEL_64KB_PAGE, ATMEL_MAX_TRANSFER_SIZE); + chunks = fu_chunk_array_new (NULL, maximum_size, address, + ATMEL_64KB_PAGE, ATMEL_MAX_TRANSFER_SIZE); /* update UI */ dfu_target_set_action (target, FWUPD_STATUS_DEVICE_READ); /* process each chunk */ - chunks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); - for (guint i = 0; i < packets->len; i++) { - GBytes *chunk_tmp = NULL; - const DfuChunkedPacket *packet = g_ptr_array_index (packets, i); + blobs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); + for (guint i = 0; i < chunks->len; i++) { + GBytes *blob_tmp = NULL; + const FuChunk *chk = g_ptr_array_index (chunks, i); /* select page if required */ - if (packet->page != page_last) { + if (chk->page != page_last) { if (dfu_device_has_quirk (dfu_target_get_device (target), DFU_DEVICE_QUIRK_LEGACY_PROTOCOL)) { if (!dfu_target_avr_select_memory_page (target, - packet->page, + chk->page, error)) return NULL; } else { if (!dfu_target_avr32_select_memory_page (target, - packet->page, + chk->page, error)) return NULL; } - page_last = packet->page; + page_last = chk->page; } /* prepare to read */ if (!dfu_target_avr_read_memory (target, - packet->address, - packet->address + packet->data_sz - 1, + chk->address, + chk->address + chk->data_sz - 1, error)) return NULL; /* upload data */ - g_debug ("requesting %i bytes from the hardware", - ATMEL_MAX_TRANSFER_SIZE); - chunk_tmp = dfu_target_upload_chunk (target, i, - ATMEL_MAX_TRANSFER_SIZE, - error); - if (chunk_tmp == NULL) + g_debug ("requesting %i bytes from the hardware for chunk 0x%x", + ATMEL_MAX_TRANSFER_SIZE, i); + blob_tmp = dfu_target_upload_chunk (target, i, + ATMEL_MAX_TRANSFER_SIZE, + error); + if (blob_tmp == NULL) return NULL; - g_ptr_array_add (chunks, chunk_tmp); + g_ptr_array_add (blobs, blob_tmp); /* this page has valid data */ - if (!dfu_utils_bytes_is_empty (chunk_tmp)) { + if (!fu_common_bytes_is_empty (blob_tmp)) { g_debug ("chunk %u has data (page %" G_GUINT32_FORMAT ")", - i, packet->page); + i, chk->page); chunk_valid = i; } else { g_debug ("chunk %u is empty", i); } /* update UI */ - dfu_target_set_percentage (target, i + 1, packets->len); + dfu_target_set_percentage (target, i + 1, chunks->len); } /* done */ @@ -701,16 +721,16 @@ /* truncate the image if any sectors are empty, i.e. all 0xff */ if (chunk_valid == G_MAXUINT) { - g_debug ("all %u chunks are empty", chunks->len); + g_debug ("all %u chunks are empty", blobs->len); g_ptr_array_set_size (chunks, 0); - } else if (chunks->len != chunk_valid + 1) { + } else if (blobs->len != chunk_valid + 1) { g_debug ("truncating chunks from %u to %u", - chunks->len, chunk_valid + 1); - g_ptr_array_set_size (chunks, chunk_valid + 1); + blobs->len, chunk_valid + 1); + g_ptr_array_set_size (blobs, chunk_valid + 1); } /* create element of required size */ - contents = dfu_utils_bytes_join_array (chunks); + contents = dfu_utils_bytes_join_array (blobs); if (expected_size > 0 && g_bytes_get_size (contents) > expected_size) { contents_truncated = g_bytes_new_from_bytes (contents, 0x0, expected_size); } else { diff -Nru fwupd-1.0.9/plugins/dfu/dfu-target-avr.h fwupd-1.2.10/plugins/dfu/dfu-target-avr.h --- fwupd-1.0.9/plugins/dfu/dfu-target-avr.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-target-avr.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_TARGET_AVR_H -#define __DFU_TARGET_AVR_H +#pragma once #include #include @@ -26,5 +24,3 @@ DfuTarget *dfu_target_avr_new (void); G_END_DECLS - -#endif /* __DFU_TARGET_AVR_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-target.c fwupd-1.2.10/plugins/dfu/dfu-target.c --- fwupd-1.0.9/plugins/dfu/dfu-target.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-target.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -1043,7 +1042,7 @@ if (zone_cur == zone_last) continue; - /* get the size of the entire continous zone */ + /* get the size of the entire continuous zone */ zone_size = dfu_target_get_size_of_zone (target, zone_cur); zone_last = zone_cur; diff -Nru fwupd-1.0.9/plugins/dfu/dfu-target.h fwupd-1.2.10/plugins/dfu/dfu-target.h --- fwupd-1.0.9/plugins/dfu/dfu-target.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-target.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_TARGET_H -#define __DFU_TARGET_H +#pragma once #include #include @@ -32,7 +30,7 @@ * @DFU_TARGET_TRANSFER_FLAG_ANY_CIPHER: Allow any cipher kinds to be downloaded * @DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC: Automatically detect the address to use * - * The optional flags used for transfering firmware. + * The optional flags used for transferring firmware. **/ typedef enum { DFU_TARGET_TRANSFER_FLAG_NONE = 0, @@ -92,5 +90,3 @@ DfuCipherKind dfu_target_get_cipher_kind (DfuTarget *target); G_END_DECLS - -#endif /* __DFU_TARGET_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-target-private.h fwupd-1.2.10/plugins/dfu/dfu-target-private.h --- fwupd-1.0.9/plugins/dfu/dfu-target-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-target-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_TARGET_PRIVATE_H -#define __DFU_TARGET_PRIVATE_H +#pragma once #include @@ -57,5 +55,3 @@ GError **error); G_END_DECLS - -#endif /* __DFU_TARGET_PRIVATE_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-target-stm.c fwupd-1.2.10/plugins/dfu/dfu-target-stm.c --- fwupd-1.0.9/plugins/dfu/dfu-target-stm.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-target-stm.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -11,7 +10,6 @@ #include #include -#include "dfu-chunked.h" #include "dfu-common.h" #include "dfu-sector.h" #include "dfu-target-stm.h" @@ -119,7 +117,7 @@ g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "memory sector at 0x%04x is not readble", + "memory sector at 0x%04x is not readable", (guint) offset); return NULL; } diff -Nru fwupd-1.0.9/plugins/dfu/dfu-target-stm.h fwupd-1.2.10/plugins/dfu/dfu-target-stm.h --- fwupd-1.0.9/plugins/dfu/dfu-target-stm.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-target-stm.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __DFU_TARGET_STM_H -#define __DFU_TARGET_STM_H +#pragma once #include #include @@ -26,5 +24,3 @@ DfuTarget *dfu_target_stm_new (void); G_END_DECLS - -#endif /* __DFU_TARGET_STM_H */ diff -Nru fwupd-1.0.9/plugins/dfu/dfu-tool.c fwupd-1.2.10/plugins/dfu/dfu-tool.c --- fwupd-1.0.9/plugins/dfu/dfu-tool.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/dfu-tool.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -12,7 +11,6 @@ #include #include #include -#include #include "dfu-cipher-xtea.h" #include "dfu-device-private.h" @@ -51,6 +49,7 @@ 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) @@ -253,7 +252,7 @@ g_autoptr(DfuDevice) device = dfu_device_new (usb_device); fu_device_set_quirks (FU_DEVICE (device), priv->quirks); dfu_device_set_usb_context (device, usb_context); - if (fu_usb_device_probe (FU_USB_DEVICE (device), NULL)) + if (fu_device_probe (FU_DEVICE (device), NULL)) return g_steal_pointer (&device); } @@ -481,7 +480,7 @@ 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); @@ -676,7 +675,7 @@ if (cnt == 0) { g_set_error_literal (error, FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, + FWUPD_ERROR_NOT_FOUND, "search string was not found"); return FALSE; } @@ -2035,10 +2034,10 @@ device = dfu_device_new (usb_device); fu_device_set_quirks (FU_DEVICE (device), priv->quirks); dfu_device_set_usb_context (device, usb_context); - if (!fu_usb_device_probe (FU_USB_DEVICE (device), NULL)) + 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), + FWUPD_VERSION_FORMAT_BCD); g_print ("%s %04x:%04x [v%s]:\n", /* TRANSLATORS: detected a DFU device */ _("Found"), @@ -2157,7 +2156,7 @@ if (priv->transfer_size > 0) dfu_device_set_transfer_size (device, priv->transfer_size); - /* detatch */ + /* detach */ locker = fu_device_locker_new (device, error); if (locker == NULL) return FALSE; @@ -2187,15 +2186,15 @@ g_autoptr(GOptionContext) context = NULL; const GOptionEntry options[] = { { "version", '\0', 0, G_OPTION_ARG_NONE, &version, - "Print the version number", NULL }, + _("Print the version number"), NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, - "Print verbose debug statements", NULL }, + _("Print verbose debug statements"), NULL }, { "device", 'd', 0, G_OPTION_ARG_STRING, &priv->device_vid_pid, - "Specify Vendor/Product ID(s) of DFU device", "VID:PID" }, + _("Specify Vendor/Product ID(s) of DFU device"), "VID:PID" }, { "transfer-size", 't', 0, G_OPTION_ARG_STRING, &priv->transfer_size, - "Specify the number of bytes per USB transfer", "BYTES" }, + _("Specify the number of bytes per USB transfer"), "BYTES" }, { "force", '\0', 0, G_OPTION_ARG_NONE, &priv->force, - "Force the action ignoring all warnings", NULL }, + _("Force the action ignoring all warnings"), NULL }, { NULL} }; @@ -2428,6 +2427,5 @@ } /* success/ */ - g_object_unref (priv->progressbar); return EXIT_SUCCESS; } diff -Nru fwupd-1.0.9/plugins/dfu/fu-plugin-dfu.c fwupd-1.2.10/plugins/dfu/fu-plugin-dfu.c --- fwupd-1.0.9/plugins/dfu/fu-plugin-dfu.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/fu-plugin-dfu.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,13 +6,19 @@ #include "config.h" -#include - -#include "fu-plugin.h" #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, @@ -32,13 +37,13 @@ } gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *dev, GError **error) { g_autoptr(DfuDevice) device = NULL; g_autoptr(FuDeviceLocker) locker = NULL; /* open the device */ - device = dfu_device_new (usb_device); + device = dfu_device_new (fu_usb_device_get_dev (dev)); fu_device_set_quirks (FU_DEVICE (device), fu_plugin_get_quirks (plugin)); dfu_device_set_usb_context (device, fu_plugin_get_usb_context (plugin)); locker = fu_device_locker_new (device, error); @@ -85,10 +90,9 @@ /* detach and USB reset */ if (!dfu_device_detach (device, error)) return FALSE; - if (!dfu_device_wait_for_replug (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error)) - return FALSE; - /* success */ + /* wait for replug */ + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } @@ -113,10 +117,9 @@ /* attach it */ if (!dfu_device_attach (device, error)) return FALSE; - if (!dfu_device_wait_for_replug (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error)) - return FALSE; - /* success */ + /* wait for replug */ + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } diff -Nru fwupd-1.0.9/plugins/dfu/meson.build fwupd-1.2.10/plugins/dfu/meson.build --- fwupd-1.0.9/plugins/dfu/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -6,8 +6,8 @@ dfu = static_library( 'dfu', + fu_hash, sources : [ - 'dfu-chunked.c', 'dfu-cipher-xtea.c', 'dfu-common.c', 'dfu-device.c', @@ -27,10 +27,13 @@ 'dfu-target-avr.c', ], dependencies : [ - appstream_glib, giounix, libm, gusb, + gudev, + ], + link_with : [ + libfwupdprivate, ], c_args : cargs, include_directories : [ @@ -41,6 +44,7 @@ ) shared_module('fu_plugin_dfu', + fu_hash, sources : [ 'fu-plugin-dfu.c', ], @@ -56,12 +60,14 @@ plugin_deps, ], link_with : [ + libfwupdprivate, dfu, ], ) dfu_tool = executable( 'dfu-tool', + fu_hash, sources : [ 'dfu-tool.c', ], @@ -72,14 +78,14 @@ include_directories('../../libfwupd'), ], dependencies : [ - appstream_glib, + libxmlb, giounix, libm, gusb, + gudev, ], link_with : [ dfu, - fwupd, libfwupdprivate, ], c_args : cargs, @@ -112,6 +118,7 @@ cargs += '-DTESTDATADIR="' + testdatadir + '"' e = executable( 'dfu-self-test', + fu_hash, sources : [ 'dfu-self-test.c' ], @@ -122,14 +129,14 @@ include_directories('../../src'), ], dependencies : [ - appstream_glib, + libxmlb, gio, gusb, + gudev, libm, ], link_with : [ dfu, - fwupd, libfwupdprivate, ], c_args : cargs diff -Nru fwupd-1.0.9/plugins/dfu/README.md fwupd-1.2.10/plugins/dfu/README.md --- fwupd-1.0.9/plugins/dfu/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/dfu/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -6,3 +6,33 @@ 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 -Nru fwupd-1.0.9/plugins/ebitdo/data/m30.txt fwupd-1.2.10/plugins/ebitdo/data/m30.txt --- fwupd-1.0.9/plugins/ebitdo/data/m30.txt 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/data/m30.txt 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,168 @@ +Bus 002 Device 008: ID 2dc8:5006 8BitDo M30 gamepad +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x2dc8 8BitDo + idProduct 0x5006 M30 gamepad + bcdDevice 0.01 + iManufacturer 1 8Bitdo + iProduct 2 8BitDo M30 gamepad + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0029 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 480mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 123 + Report Descriptor: (length is 123) + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Local ): Usage, data= [ 0x05 ] 5 + Gamepad + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0x01 ] 1 + Item(Global): Physical Minimum, data= [ 0x00 ] 0 + Item(Global): Physical Maximum, data= [ 0x01 ] 1 + Item(Global): Report Size, data= [ 0x01 ] 1 + Item(Global): Report Count, data= [ 0x0f ] 15 + Item(Global): Usage Page, data= [ 0x09 ] 9 + Buttons + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + Button 1 (Primary) + Item(Local ): Usage Maximum, data= [ 0x0f ] 15 + (null) + Item(Main ): Input, data= [ 0x02 ] 2 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Main ): Input, data= [ 0x01 ] 1 + Constant Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Global): Logical Maximum, data= [ 0x07 ] 7 + Item(Global): Physical Maximum, data= [ 0x3b 0x01 ] 315 + Item(Global): Report Size, data= [ 0x04 ] 4 + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Global): Unit, data= [ 0x14 ] 20 + System: English Rotation, Unit: Degrees + Item(Local ): Usage, data= [ 0x39 ] 57 + Hat Switch + Item(Main ): Input, data= [ 0x42 ] 66 + Data Variable Absolute No_Wrap Linear + Preferred_State Null_State Non_Volatile Bitfield + Item(Global): Unit, data= [ 0x00 ] 0 + System: None, Unit: (None) + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Main ): Input, data= [ 0x01 ] 1 + Constant Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Global): Physical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x30 ] 48 + Direction-X + Item(Local ): Usage, data= [ 0x31 ] 49 + Direction-Y + Item(Local ): Usage, data= [ 0x32 ] 50 + Direction-Z + Item(Local ): Usage, data= [ 0x35 ] 53 + Rotate-Z + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x04 ] 4 + Item(Main ): Input, data= [ 0x02 ] 2 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Usage Page, data= [ 0x02 ] 2 + Simulation Controls + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0xc4 ] 196 + Accelerator + Item(Local ): Usage, data= [ 0xc5 ] 197 + Brake + Item(Global): Report Count, data= [ 0x02 ] 2 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Main ): Input, data= [ 0x02 ] 2 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Usage Page, data= [ 0x08 ] 8 + LEDs + Item(Local ): Usage, data= [ 0x43 ] 67 + Slow Blink On Time + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Global): Physical Minimum, data= [ 0x00 ] 0 + Item(Global): Physical Maximum, data= [ 0xff 0x00 ] 255 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x02 ] 2 + Item(Main ): Output, data= [ 0x82 ] 130 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Volatile Bitfield + Item(Local ): Usage, data= [ 0x44 ] 68 + Slow Blink Off Time + Item(Main ): Output, data= [ 0x82 ] 130 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Volatile Bitfield + Item(Local ): Usage, data= [ 0x45 ] 69 + Fast Blink On Time + Item(Main ): Output, data= [ 0x82 ] 130 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Volatile Bitfield + Item(Local ): Usage, data= [ 0x46 ] 70 + Fast Blink Off Time + Item(Main ): Output, data= [ 0x82 ] 130 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Volatile Bitfield + Item(Main ): End Collection, data=none + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 16 +Device Status: 0x0000 + (Bus Powered) diff -Nru fwupd-1.0.9/plugins/ebitdo/ebitdo.quirk fwupd-1.2.10/plugins/ebitdo/ebitdo.quirk --- fwupd-1.0.9/plugins/ebitdo/ebitdo.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/ebitdo.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -1,41 +1,92 @@ -[FuEbitdoDevice] - # bootloader -USB\VID_0483&PID_5750=bootloader -USB\VID_2DC8&PID_5750=bootloader +[DeviceInstanceId=USB\VID_0483&PID_5750] +Plugin = ebitdo +Flags = is-bootloader +[DeviceInstanceId=USB\VID_2DC8&PID_5750] +Plugin = ebitdo +Flags = is-bootloader +InstallDuration = 120 +[DeviceInstanceId=USB\VID_0483&PID_5760] +Plugin = ebitdo +Flags = is-bootloader +InstallDuration = 120 # FC30 -USB\VID_1235&PID_AB11=none -USB\VID_2DC8&PID_AB11=none +[DeviceInstanceId=USB\VID_1235&PID_AB11] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_AB11] +Plugin = ebitdo +Flags = none +InstallDuration = 120 # NES30 -USB\VID_1235&PID_AB12=none -USB\VID_2DC8&PID_AB12=none +[DeviceInstanceId=USB\VID_1235&PID_AB12] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_AB12] +Plugin = ebitdo +Flags = none +InstallDuration = 120 # SFC30 -USB\VID_1235&PID_AB21=none -USB\VID_2DC8&PID_AB21=none +[DeviceInstanceId=USB\VID_1235&PID_AB21] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_AB21] +Plugin = ebitdo +Flags = none +InstallDuration = 120 # SNES30 -USB\VID_1235&PID_AB20=none -USB\VID_2DC8&PID_AB20=none +[DeviceInstanceId=USB\VID_1235&PID_AB20] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_AB20] +Plugin = ebitdo +Flags = none +InstallDuration = 120 # FC30PRO -USB\VID_1002&PID_9000=none -USB\VID_2DC8&PID_9000=none +[DeviceInstanceId=USB\VID_1002&PID_9000] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_9000] +Plugin = ebitdo +Flags = none +InstallDuration = 120 # NES30PRO -USB\VID_2002&PID_9000=none -USB\VID_2DC8&PID_9001=none +[DeviceInstanceId=USB\VID_2002&PID_9000] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_9001] +Plugin = ebitdo +Flags = none +InstallDuration = 120 # FC30_ARCADE -USB\VID_8000&PID_1002=none -USB\VID_2DC8&PID_1002=none +[DeviceInstanceId=USB\VID_8000&PID_1002] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_1002] +Plugin = ebitdo +Flags = none +InstallDuration = 120 # SF30 PRO/SN30 PRO ## Dinput mode (Start + B) -USB\VID_2DC8&PID_6000=none -USB\VID_2DC8&PID_6001=none -## Xinput mode (Start + X) -USB\VID_045E&PID_028E=none +[DeviceInstanceId=USB\VID_2DC8&PID_6000] +Plugin = ebitdo +Flags = none +[DeviceInstanceId=USB\VID_2DC8&PID_6001] +Plugin = ebitdo +Flags = none +InstallDuration = 120 + +# M30 +[DeviceInstanceId=USB\VID_2DC8&PID_5006] +Plugin = ebitdo +Flags = none +InstallDuration = 120 diff -Nru fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-common.c fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-common.c --- fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -66,20 +65,6 @@ } 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) { g_print ("PktLength: 0x%02x\n", hdr->pkt_len); diff -Nru fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-common.h fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-common.h --- fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_EBITDO_COMMON_H -#define __FU_EBITDO_COMMON_H +#pragma once #include @@ -71,8 +69,3 @@ 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 -Nru fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-device.c fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-device.c --- fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -8,7 +7,6 @@ #include "config.h" #include -#include #include "fu-ebitdo-common.h" #include "fu-ebitdo-device.h" @@ -68,7 +66,7 @@ /* 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); } @@ -129,7 +127,7 @@ /* 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); } @@ -217,30 +215,30 @@ fu_ebitdo_device_set_version (FuEbitdoDevice *self, guint32 version) { g_autofree gchar *tmp = NULL; - tmp = g_strdup_printf ("%.2f", version / 100.f); - fu_device_set_version (FU_DEVICE (self), tmp); + tmp = g_strdup_printf ("%u.%02u", version / 100, version % 100); + fu_device_set_version (FU_DEVICE (self), tmp, FWUPD_VERSION_FORMAT_PAIR); } static gboolean fu_ebitdo_device_validate (FuEbitdoDevice *self, GError **error) { - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); - guint8 idx; - g_autofree gchar *ven = NULL; + const gchar *ven; const gchar *whitelist[] = { "8Bitdo", "SFC30", NULL }; /* this is a new, always valid, VID */ - if (g_usb_device_get_vid (usb_device) == 0x2dc8) + if (fu_usb_device_get_vid (FU_USB_DEVICE (self)) == 0x2dc8) return TRUE; /* verify the vendor prefix against a whitelist */ - idx = g_usb_device_get_manufacturer_index (usb_device); - ven = g_usb_device_get_string_descriptor (usb_device, idx, error); + ven = fu_device_get_vendor (FU_DEVICE (self)); if (ven == NULL) { - g_prefix_error (error, "could not check vendor descriptor: "); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "could not check vendor descriptor: "); return FALSE; } for (guint i = 0; whitelist[i] != NULL; i++) { @@ -258,11 +256,8 @@ static gboolean fu_ebitdo_device_open (FuUsbDevice *device, GError **error) { - FuEbitdoDevice *self = FU_EBITDO_DEVICE (device); GUsbDevice *usb_device = fu_usb_device_get_dev (device); - gdouble tmp; - guint32 version_tmp = 0; - guint32 serial_tmp[9] = {0}; + FuEbitdoDevice *self = FU_EBITDO_DEVICE (device); /* open, then ensure this is actually 8Bitdo hardware */ if (!fu_ebitdo_device_validate (self, error)) @@ -273,6 +268,18 @@ return FALSE; } + /* success */ + return TRUE; +} + +static gboolean +fu_ebitdo_device_setup (FuDevice *device, GError **error) +{ + FuEbitdoDevice *self = FU_EBITDO_DEVICE (device); + gdouble tmp; + guint32 version_tmp = 0; + guint32 serial_tmp[9] = {0}; + /* in firmware mode */ if (!fu_device_has_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { if (!fu_ebitdo_device_send (self, @@ -340,7 +347,10 @@ } static gboolean -fu_ebitdo_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +fu_ebitdo_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) { FuEbitdoDevice *self = FU_EBITDO_DEVICE (device); FuEbitdoFirmwareHeader *hdr; @@ -382,6 +392,7 @@ case 0x6000: /* SF30 pro: Dinput mode */ case 0x6001: /* SN30 pro: Dinput mode */ case 0x028e: /* SF30/SN30 pro: Xinput mode */ + case 0x5006: /* M30 */ g_string_append (msg, "press and hold L1+R1+START for 3 seconds " "until the LED on top blinks red, "); break; @@ -538,7 +549,6 @@ } /* success! */ - fu_device_set_status (device, FWUPD_STATUS_IDLE); return TRUE; } @@ -546,21 +556,6 @@ fu_ebitdo_device_probe (FuUsbDevice *device, GError **error) { FuEbitdoDevice *self = FU_EBITDO_DEVICE (device); - const gchar *quirk_str; - - /* devices have to be whitelisted */ - quirk_str = fu_device_get_plugin_hints (FU_DEVICE (device)); - if (quirk_str == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported with this device"); - return FALSE; - } - if (g_strcmp0 (quirk_str, "bootloader") == 0) - fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - else - fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); /* allowed, but requires manual bootloader step */ fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); @@ -576,13 +571,10 @@ /* only the bootloader can do the update */ if (!fu_device_has_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - fu_device_add_guid (FU_DEVICE (device), "USB\\VID_0483&PID_5750"); - fu_device_add_guid (FU_DEVICE (device), "USB\\VID_2DC8&PID_5750"); + fu_device_add_counterpart_guid (FU_DEVICE (device), "USB\\VID_0483&PID_5750"); + fu_device_add_counterpart_guid (FU_DEVICE (device), "USB\\VID_2DC8&PID_5750"); fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); - } else { - fu_device_remove_flag (FU_DEVICE (device), - FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); } /* success */ @@ -600,6 +592,7 @@ FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); klass_device->write_firmware = fu_ebitdo_device_write_firmware; + klass_device->setup = fu_ebitdo_device_setup; klass_usb_device->open = fu_ebitdo_device_open; klass_usb_device->probe = fu_ebitdo_device_probe; } @@ -614,11 +607,10 @@ * Since: 0.1.0 **/ FuEbitdoDevice * -fu_ebitdo_device_new (GUsbDevice *usb_device) +fu_ebitdo_device_new (FuUsbDevice *device) { FuEbitdoDevice *self; - self = g_object_new (FU_TYPE_EBITDO_DEVICE, - "usb-device", usb_device, - NULL); + self = g_object_new (FU_TYPE_EBITDO_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); return FU_EBITDO_DEVICE (self); } diff -Nru fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-device.h fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-device.h --- fwupd-1.0.9/plugins/ebitdo/fu-ebitdo-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/fu-ebitdo-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_EBITDO_DEVICE_H -#define __FU_EBITDO_DEVICE_H - -#include -#include +#pragma once #include "fu-plugin.h" @@ -18,11 +13,9 @@ #define FU_TYPE_EBITDO_DEVICE (fu_ebitdo_device_get_type ()) G_DECLARE_FINAL_TYPE (FuEbitdoDevice, fu_ebitdo_device, FU, EBITDO_DEVICE, FuUsbDevice) -FuEbitdoDevice *fu_ebitdo_device_new (GUsbDevice *usb_device); +FuEbitdoDevice *fu_ebitdo_device_new (FuUsbDevice *device); /* getters */ const guint32 *fu_ebitdo_device_get_serial (FuEbitdoDevice *device); G_END_DECLS - -#endif /* __FU_EBITDO_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/ebitdo/fu-plugin-ebitdo.c fwupd-1.2.10/plugins/ebitdo/fu-plugin-ebitdo.c --- fwupd-1.0.9/plugins/ebitdo/fu-plugin-ebitdo.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/fu-plugin-ebitdo.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -9,24 +8,30 @@ #include "fu-ebitdo-device.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.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.8bitdo"); +} + gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) { g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuEbitdoDevice) device = NULL; + g_autoptr(FuEbitdoDevice) dev = NULL; /* open the device */ - device = fu_ebitdo_device_new (usb_device); - fu_device_set_quirks (FU_DEVICE (device), fu_plugin_get_quirks (plugin)); - locker = fu_device_locker_new (device, error); + dev = fu_ebitdo_device_new (device); + locker = fu_device_locker_new (dev, error); if (locker == NULL) return FALSE; /* success */ - fu_plugin_device_add (plugin, FU_DEVICE (device)); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } @@ -40,7 +45,6 @@ GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (dev)); FuEbitdoDevice *ebitdo_dev = FU_EBITDO_DEVICE (dev); g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GUsbDevice) usb_device2 = NULL; /* get version */ if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { @@ -55,7 +59,7 @@ locker = fu_device_locker_new (ebitdo_dev, error); if (locker == NULL) return FALSE; - if (!fu_device_write_firmware (FU_DEVICE (ebitdo_dev), blob_fw, error)) + if (!fu_device_write_firmware (FU_DEVICE (ebitdo_dev), blob_fw, flags, error)) return FALSE; /* when doing a soft-reboot the device does not re-enumerate properly @@ -65,16 +69,9 @@ g_prefix_error (error, "failed to force-reset device: "); return FALSE; } - g_clear_object (&locker); - usb_device2 = g_usb_context_wait_for_replug (fu_plugin_get_usb_context (plugin), - usb_device, 10000, error); - if (usb_device2 == NULL) { - g_prefix_error (error, "device did not come back: "); - return FALSE; - } - fu_usb_device_set_dev (FU_USB_DEVICE (ebitdo_dev), usb_device2); - /* success */ + /* wait for replug */ + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } diff -Nru fwupd-1.0.9/plugins/ebitdo/meson.build fwupd-1.2.10/plugins/ebitdo/meson.build --- fwupd-1.0.9/plugins/ebitdo/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -5,6 +5,7 @@ ) shared_module('fu_plugin_ebitdo', + fu_hash, sources : [ 'fu-plugin-ebitdo.c', 'fu-ebitdo-common.c', @@ -17,6 +18,9 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff -Nru fwupd-1.0.9/plugins/ebitdo/README.md fwupd-1.2.10/plugins/ebitdo/README.md --- fwupd-1.0.9/plugins/ebitdo/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/ebitdo/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -11,3 +11,23 @@ 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 -Nru fwupd-1.0.9/plugins/fastboot/data/android/flashfile.xml fwupd-1.2.10/plugins/fastboot/data/android/flashfile.xml --- fwupd-1.0.9/plugins/fastboot/data/android/flashfile.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/data/android/flashfile.xml 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru fwupd-1.0.9/plugins/fastboot/data/lsusb.txt fwupd-1.2.10/plugins/fastboot/data/lsusb.txt --- fwupd-1.0.9/plugins/fastboot/data/lsusb.txt 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/data/lsusb.txt 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/fastboot/data/qfil/partition_nand.xml fwupd-1.2.10/plugins/fastboot/data/qfil/partition_nand.xml --- fwupd-1.0.9/plugins/fastboot/data/qfil/partition_nand.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/data/qfil/partition_nand.xml 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,21 @@ + + + + 0xAA7D1B9A + 0x1F7D48BC + + 0x4 + + + 0:SBL + 0x8 + 0x2 + 0 + 0xFF + 0x01 + 0x00 + 0xFE + sbl1.mbn + + + diff -Nru fwupd-1.0.9/plugins/fastboot/fastboot.quirk fwupd-1.2.10/plugins/fastboot/fastboot.quirk --- fwupd-1.0.9/plugins/fastboot/fastboot.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/fastboot.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,3 @@ +# All fastboot devices +[DeviceInstanceId=USB\CLASS_FF&SUBCLASS_42&PROT_03] +Plugin = fastboot diff -Nru fwupd-1.0.9/plugins/fastboot/fu-fastboot-device.c fwupd-1.2.10/plugins/fastboot/fu-fastboot-device.c --- fwupd-1.0.9/plugins/fastboot/fu-fastboot-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/fu-fastboot-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,714 @@ +/* + * 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, + FwupdInstallFlags flags, + 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 -Nru fwupd-1.0.9/plugins/fastboot/fu-fastboot-device.h fwupd-1.2.10/plugins/fastboot/fu-fastboot-device.h --- fwupd-1.0.9/plugins/fastboot/fu-fastboot-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/fu-fastboot-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/fastboot/fu-plugin-fastboot.c fwupd-1.2.10/plugins/fastboot/fu-plugin-fastboot.c --- fwupd-1.0.9/plugins/fastboot/fu-plugin-fastboot.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/fu-plugin-fastboot.c 2019-07-15 18:25:54.000000000 +0000 @@ -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, flags, 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 -Nru fwupd-1.0.9/plugins/fastboot/meson.build fwupd-1.2.10/plugins/fastboot/meson.build --- fwupd-1.0.9/plugins/fastboot/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/fastboot/README.md fwupd-1.2.10/plugins/fastboot/README.md --- fwupd-1.0.9/plugins/fastboot/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/fastboot/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 firmware 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 -Nru fwupd-1.0.9/plugins/flashrom/example/build.sh fwupd-1.2.10/plugins/flashrom/example/build.sh --- fwupd-1.0.9/plugins/flashrom/example/build.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/example/build.sh 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,4 @@ +#/bin/sh +appstream-util validate-relax com.Flashrom.Laptop.metainfo.xml +tar -cf firmware.tar startup.sh random-tool +gcab --create --nopath Flashrom-Laptop-1.2.3.cab firmware.tar com.Flashrom.Laptop.metainfo.xml diff -Nru fwupd-1.0.9/plugins/flashrom/example/com.Flashrom.Laptop.metainfo.xml fwupd-1.2.10/plugins/flashrom/example/com.Flashrom.Laptop.metainfo.xml --- fwupd-1.0.9/plugins/flashrom/example/com.Flashrom.Laptop.metainfo.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/example/com.Flashrom.Laptop.metainfo.xml 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,42 @@ + + + + com.Flashrom.Laptop.firmware + Flashrom Laptop Firmware + System firmware for a Flashrom laptop + +

+ The laptop can be updated using flashrom. +

+
+ + + a0ce5085-2dea-5086-ae72-45810a186ad0 + + http://www.bbc.co.uk/ + CC0-1.0 + Proprietary + Flashrom + + + + +

+ This release updates a frobnicator to frob faster. +

+
+
+
+ + + + startup.sh + firmware.bin + + + + + org.freedesktop.fwupd + + +
diff -Nru fwupd-1.0.9/plugins/flashrom/example/random-tool fwupd-1.2.10/plugins/flashrom/example/random-tool --- fwupd-1.0.9/plugins/flashrom/example/random-tool 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/example/random-tool 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +#/bin/sh +echo "hello from the sandbox" diff -Nru fwupd-1.0.9/plugins/flashrom/example/startup.sh fwupd-1.2.10/plugins/flashrom/example/startup.sh --- fwupd-1.0.9/plugins/flashrom/example/startup.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/example/startup.sh 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,10 @@ +#/bin/sh + +# do something with the old firmware +sha1sum /boot/flashrom-librem15v3.bin + +# run a random tool +./random-tool + +# this is the deliverable +cp /boot/flashrom-librem15v3.bin firmware.bin diff -Nru fwupd-1.0.9/plugins/flashrom/flashrom.quirk fwupd-1.2.10/plugins/flashrom/flashrom.quirk --- fwupd-1.0.9/plugins/flashrom/flashrom.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/flashrom.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,8 @@ +# Purism +[HwId=a0ce5085-2dea-5086-ae72-45810a186ad0] +DeviceId=librem15v3 + +# Libretrend +[HwId=52b68c34-6b31-5ecc-8a5c-de37e666ccd5] +DeviceId=LT1000 +VersionFormat=quad diff -Nru fwupd-1.0.9/plugins/flashrom/fu-plugin-flashrom.c fwupd-1.2.10/plugins/flashrom/fu-plugin-flashrom.c --- fwupd-1.0.9/plugins/flashrom/fu-plugin-flashrom.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/fu-plugin-flashrom.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,263 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include + +#include "fu-plugin-vfuncs.h" +#include "libflashrom.h" + +#define SELFCHECK_TRUE 1 + +struct FuPluginData { + gsize flash_size; + struct flashrom_flashctx *flashctx; + struct flashrom_layout *layout; + struct flashrom_programmer *flashprog; +}; + +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 +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + flashrom_layout_release (data->layout); + flashrom_programmer_shutdown (data->flashprog); + flashrom_flash_release (data->flashctx); +} + +static int +fu_plugin_flashrom_debug_cb (enum flashrom_log_level lvl, const char *fmt, va_list args) +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" + g_autofree gchar *tmp = g_strdup_vprintf (fmt, args); +#pragma clang diagnostic pop + switch (lvl) { + case FLASHROM_MSG_ERROR: + case FLASHROM_MSG_WARN: + g_warning ("%s", tmp); + break; + case FLASHROM_MSG_INFO: + g_debug ("%s", tmp); + break; + case FLASHROM_MSG_DEBUG: + case FLASHROM_MSG_DEBUG2: + if (g_getenv ("FWUPD_FLASHROM_VERBOSE") != NULL) + g_debug ("%s", tmp); + break; + case FLASHROM_MSG_SPEW: + break; + default: + break; + } + return 0; +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + GPtrArray *hwids = fu_plugin_get_hwids (plugin); + g_autoptr(GPtrArray) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + + for (guint i = 0; i < hwids->len; i++) { + const gchar *guid = g_ptr_array_index (hwids, i); + const gchar *quirk_str; + g_autofree gchar *quirk_key_prefixed = NULL; + quirk_key_prefixed = g_strdup_printf ("HwId=%s", guid); + quirk_str = fu_plugin_lookup_quirk_by_id (plugin, + quirk_key_prefixed, + "DeviceId"); + if (quirk_str != NULL) { + 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); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_name (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_NAME)); + fu_device_set_vendor (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER)); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_ENSURE_SEMVER); + fu_device_set_version (dev, + fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VERSION), + FWUPD_VERSION_FORMAT_UNKNOWN); + fu_device_add_guid (dev, guid); + g_ptr_array_add (devices, g_steal_pointer (&dev)); + break; + } + } + + /* nothing to do, so don't bother initializing flashrom */ + if (devices->len == 0) + return TRUE; + + /* actually probe hardware to check for support */ + if (flashrom_init (SELFCHECK_TRUE)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flashrom initialization error"); + return FALSE; + } + flashrom_set_log_callback (fu_plugin_flashrom_debug_cb); + if (flashrom_programmer_init (&data->flashprog, "internal", NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "programmer initialization failed"); + return FALSE; + } + if (flashrom_flash_probe (&data->flashctx, data->flashprog, NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash probe failed"); + return FALSE; + } + data->flash_size = flashrom_flash_getsize (data->flashctx); + if (data->flash_size == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash size zero"); + return FALSE; + } + + /* add devices */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index (devices, i); + fu_plugin_device_add (plugin, dev); + fu_plugin_cache_add (plugin, fu_device_get_id (dev), dev); + } + return TRUE; +} + +gboolean +fu_plugin_update_prepare (FuPlugin *plugin, + FwupdInstallFlags flags, + FuDevice *device, + GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autofree gchar *firmware_orig = NULL; + g_autofree gchar *basename = NULL; + + /* not us */ + if (fu_plugin_cache_lookup (plugin, fu_device_get_id (device)) == NULL) + return TRUE; + + /* if the original firmware doesn't exist, grab it now */ + basename = g_strdup_printf ("flashrom-%s.bin", fu_device_get_id (device)); + firmware_orig = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", + "builder", basename, NULL); + if (!fu_common_mkdir_parent (firmware_orig, error)) + return FALSE; + if (!g_file_test (firmware_orig, G_FILE_TEST_EXISTS)) { + g_autofree guint8 *newcontents = g_malloc0 (data->flash_size); + g_autoptr(GBytes) buf = NULL; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ); + if (flashrom_image_read (data->flashctx, newcontents, data->flash_size)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to back up original firmware"); + return FALSE; + } + buf = g_bytes_new_static (newcontents, data->flash_size); + if (!fu_common_set_contents_bytes (firmware_orig, buf, error)) + return FALSE; + } + + return TRUE; +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + gsize sz = 0; + gint rc; + const guint8 *buf = g_bytes_get_data (blob_fw, &sz); + + if (flashrom_layout_read_from_ifd (&data->layout, data->flashctx, NULL, 0)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to read layout from Intel ICH descriptor"); + return FALSE; + } + + /* include bios region for safety reasons */ + if (flashrom_layout_include_region (data->layout, "bios")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid region name"); + return FALSE; + } + + /* write region */ + flashrom_layout_set (data->flashctx, data->layout); + if (sz != data->flash_size) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid image size 0x%x, expected 0x%x", + (guint) sz, (guint) data->flash_size); + return FALSE; + } + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + rc = flashrom_image_write (data->flashctx, (void *) buf, sz, NULL /* refbuffer */); + if (rc != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "image write failed, err=%i", rc); + return FALSE; + } + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY); + if (flashrom_image_verify (data->flashctx, (void *) buf, sz)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "image verify failed"); + return FALSE; + } + + /* success */ + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/flashrom/meson.build fwupd-1.2.10/plugins/flashrom/meson.build --- fwupd-1.0.9/plugins/flashrom/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,30 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginFlashrom"'] + +install_data(['flashrom.quirk'], + install_dir: join_paths(get_option('datadir'), 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_flashrom', + fu_hash, + sources : [ + 'fu-plugin-flashrom.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + dependencies : [ + plugin_deps, + libflashrom, + ], +) diff -Nru fwupd-1.0.9/plugins/flashrom/README.md fwupd-1.2.10/plugins/flashrom/README.md --- fwupd-1.0.9/plugins/flashrom/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/flashrom/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,25 @@ +Flashrom +======== + +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 -Nru fwupd-1.0.9/plugins/meson.build fwupd-1.2.10/plugins/meson.build --- fwupd-1.0.9/plugins/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,17 +1,33 @@ +subdir('ata') subdir('dfu') subdir('colorhug') subdir('ebitdo') +subdir('fastboot') subdir('steelseries') +subdir('dell-dock') subdir('nitrokey') +subdir('rts54hid') +subdir('rts54hub') +subdir('synaptics-prometheus') subdir('test') subdir('udev') subdir('unifying') subdir('upower') -subdir('wacomhid') +subdir('wacom-raw') +subdir('wacom-usb') +subdir('superio') # depends on dfu subdir('csr') +if get_option('plugin_nvme') +subdir('nvme') +endif + +if get_option('plugin_modem_manager') +subdir('modem-manager') +endif + if get_option('plugin_altos') subdir('altos') endif @@ -25,8 +41,13 @@ subdir('thunderbolt-power') endif +if get_option('plugin_redfish') +subdir('redfish') +endif + if get_option('plugin_dell') subdir('dell') +subdir('dell-esrt') endif if get_option('plugin_synaptics') @@ -36,3 +57,7 @@ if get_option('plugin_uefi') subdir('uefi') endif + +if get_option('plugin_flashrom') +subdir('flashrom') +endif diff -Nru fwupd-1.0.9/plugins/modem-manager/fu-mm-device.c fwupd-1.2.10/plugins/modem-manager/fu-mm-device.c --- fwupd-1.0.9/plugins/modem-manager/fu-mm-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/fu-mm-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,834 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-io-channel.h" +#include "fu-archive.h" +#include "fu-mm-device.h" +#include "fu-device-private.h" +#include "fu-mm-utils.h" +#include "fu-qmi-pdc-updater.h" + +/* Amount of time for the modem to be re-probed and exposed in MM after being + * uninhibited. The timeout is long enough to cover the worst case, where the + * modem boots without SIM card inserted (and therefore the initialization + * may be very slow) and also where carrier config switching is explicitly + * required (e.g. if switching from the default (DF) to generic (GC).*/ +#define FU_MM_DEVICE_REMOVE_DELAY_REPROBE 120000 /* ms */ + +struct _FuMmDevice { + FuDevice parent_instance; + MMManager *manager; + + /* ModemManager-based devices will have MMObject and inhibition_uid set, + * udev-based ones won't (as device is already inhibited) */ + MMObject *omodem; + gchar *inhibition_uid; + + /* Properties read from the ModemManager-exposed modem, and to be + * propagated to plain udev-exposed modem objects. We assume that + * the firmware upgrade operation doesn't change the USB layout, and + * therefore the USB interface of the modem device that was an + * AT-capable TTY is assumed to be the same one after the upgrade. + */ + MMModemFirmwareUpdateMethod update_methods; + gchar *detach_fastboot_at; + gint port_at_ifnum; + + /* fastboot detach handling */ + gchar *port_at; + FuIOChannel *io_channel; + + /* qmi-pdc update logic */ + gchar *port_qmi; + FuQmiPdcUpdater *qmi_pdc_updater; + GArray *qmi_pdc_active_id; + guint attach_idle; +}; + +enum { + SIGNAL_ATTACH_FINISHED, + SIGNAL_LAST +}; + +static guint signals [SIGNAL_LAST] = { 0 }; + +G_DEFINE_TYPE (FuMmDevice, fu_mm_device, FU_TYPE_DEVICE) + +static void +fu_mm_device_to_string (FuDevice *device, GString *str) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + g_string_append (str, " FuMmDevice:\n"); + if (self->port_at != NULL) { + g_string_append_printf (str, " at-port:\t\t\t%s\n", + self->port_at); + } + if (self->port_qmi != NULL) { + g_string_append_printf (str, " qmi-port:\t\t\t%s\n", + self->port_qmi); + } +} + +const gchar * +fu_mm_device_get_inhibition_uid (FuMmDevice *device) +{ + g_return_val_if_fail (FU_IS_MM_DEVICE (device), NULL); + return device->inhibition_uid; +} + +MMModemFirmwareUpdateMethod +fu_mm_device_get_update_methods (FuMmDevice *device) +{ + g_return_val_if_fail (FU_IS_MM_DEVICE (device), MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); + return device->update_methods; +} + +const gchar * +fu_mm_device_get_detach_fastboot_at (FuMmDevice *device) +{ + g_return_val_if_fail (FU_IS_MM_DEVICE (device), NULL); + return device->detach_fastboot_at; +} + +gint +fu_mm_device_get_port_at_ifnum (FuMmDevice *device) +{ + g_return_val_if_fail (FU_IS_MM_DEVICE (device), -1); + return device->port_at_ifnum; +} + +static gboolean +fu_mm_device_probe_default (FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + MMModemFirmware *modem_fw; + MMModem *modem = mm_object_peek_modem (self->omodem); + MMModemPortInfo *ports = NULL; + const gchar **device_ids; + const gchar *version; + guint n_ports = 0; + g_autoptr(MMFirmwareUpdateSettings) update_settings = NULL; + g_autofree gchar *device_sysfs_path = NULL; + + /* inhibition uid is the modem interface 'Device' property, which may + * be the device sysfs path or a different user-provided id */ + self->inhibition_uid = mm_modem_dup_device (modem); + + /* find out what update methods we should use */ + modem_fw = mm_object_peek_modem_firmware (self->omodem); + update_settings = mm_modem_firmware_get_update_settings (modem_fw); + self->update_methods = mm_firmware_update_settings_get_method (update_settings); + if (self->update_methods == MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "modem cannot be put in programming mode"); + return FALSE; + } + + /* various fastboot commands */ + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { + const gchar *tmp; + tmp = mm_firmware_update_settings_get_fastboot_at (update_settings); + if (tmp == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "modem does not set fastboot command"); + return FALSE; + } + self->detach_fastboot_at = g_strdup (tmp); + } + + /* get GUIDs */ + device_ids = mm_firmware_update_settings_get_device_ids (update_settings); + if (device_ids == NULL || device_ids[0] == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "modem did not specify any device IDs"); + return FALSE; + } + + /* get version string, which is fw_ver+config_ver */ + version = mm_firmware_update_settings_get_version (update_settings); + if (version == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "modem did not specify a firmware version"); + return FALSE; + } + + /* look for the AT and QMI/MBIM ports */ + if (!mm_modem_get_ports (modem, &ports, &n_ports)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to get port information"); + return FALSE; + } + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { + for (guint i = 0; i < n_ports; i++) { + if (ports[i].type == MM_MODEM_PORT_TYPE_AT) { + self->port_at = g_strdup_printf ("/dev/%s", ports[i].name); + break; + } + } + } + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) { + for (guint i = 0; i < n_ports; i++) { + if ((ports[i].type == MM_MODEM_PORT_TYPE_QMI) || + (ports[i].type == MM_MODEM_PORT_TYPE_MBIM)) { + self->port_qmi = g_strdup_printf ("/dev/%s", ports[i].name); + break; + } + } + } + mm_modem_port_info_array_free (ports, n_ports); + + /* an at port is required for fastboot */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && + (self->port_at == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find AT port"); + return FALSE; + } + + /* a qmi port is required for qmi-pdc */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) && + (self->port_qmi == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find QMI port"); + return FALSE; + } + + /* if we have the at port reported, get sysfs path and interface number */ + if (self->port_at != NULL) { + fu_mm_utils_get_port_info (self->port_at, &device_sysfs_path, &self->port_at_ifnum, NULL); + } else if (self->port_qmi != NULL) { + fu_mm_utils_get_port_info (self->port_qmi, &device_sysfs_path, NULL, NULL); + } else { + g_warn_if_reached (); + } + + /* if no device sysfs file, error out */ + if (device_sysfs_path == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find device sysfs path"); + return FALSE; + } + + /* add properties to fwupd device */ + fu_device_set_physical_id (device, device_sysfs_path); + if (mm_modem_get_manufacturer (modem) != NULL) + fu_device_set_vendor (device, mm_modem_get_manufacturer (modem)); + if (mm_modem_get_model (modem) != NULL) + fu_device_set_name (device, mm_modem_get_model (modem)); + fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_UNKNOWN); + for (guint i = 0; device_ids[i] != NULL; i++) + fu_device_add_instance_id (device, device_ids[i]); + + /* convert the instance IDs to GUIDs */ + fu_device_convert_instance_ids (device); + + return TRUE; +} + +static gboolean +fu_mm_device_probe_udev (FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + + /* an at port is required for fastboot */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && + (self->port_at == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find AT port"); + return FALSE; + } + + /* a qmi port is required for qmi-pdc */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) && + (self->port_qmi == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find QMI port"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_mm_device_probe (FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + + if (self->omodem) { + return fu_mm_device_probe_default (device, error); + } else { + return fu_mm_device_probe_udev (device, error); + } +} + +static gboolean +fu_mm_device_at_cmd (FuMmDevice *self, const gchar *cmd, GError **error) +{ + const gchar *buf; + gsize bufsz = 0; + g_autoptr(GBytes) at_req = NULL; + g_autoptr(GBytes) at_res = NULL; + g_autofree gchar *cmd_cr = g_strdup_printf ("%s\r\n", cmd); + + /* command */ + at_req = g_bytes_new (cmd_cr, strlen (cmd_cr)); + if (g_getenv ("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_common_dump_bytes (G_LOG_DOMAIN, "writing", at_req); + if (!fu_io_channel_write_bytes (self->io_channel, at_req, 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT, error)) { + g_prefix_error (error, "failed to write %s: ", cmd); + return FALSE; + } + + /* response */ + at_res = fu_io_channel_read_bytes (self->io_channel, -1, 1500, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, error); + if (at_res == NULL) { + g_prefix_error (error, "failed to read response for %s: ", cmd); + return FALSE; + } + if (g_getenv ("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_common_dump_bytes (G_LOG_DOMAIN, "read", at_res); + buf = g_bytes_get_data (at_res, &bufsz); + if (bufsz < 6) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to read valid response for %s", cmd); + return FALSE; + } + if (memcmp (buf, "\r\nOK\r\n", 6) != 0) { + g_autofree gchar *tmp = g_strndup (buf + 2, bufsz - 4); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to read valid response for %s: %s", + cmd, tmp); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_mm_device_io_open (FuMmDevice *self, GError **error) +{ + /* open device */ + self->io_channel = fu_io_channel_new_file (self->port_at, error); + if (self->io_channel == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_device_io_close (FuMmDevice *self, GError **error) +{ + if (!fu_io_channel_shutdown (self->io_channel, error)) + return FALSE; + g_clear_object (&self->io_channel); + return TRUE; +} + +static gboolean +fu_mm_device_detach_fastboot (FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* boot to fastboot mode */ + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) fu_mm_device_io_open, + (FuDeviceLockerFunc) fu_mm_device_io_close, + error); + if (locker == NULL) + return FALSE; + if (!fu_mm_device_at_cmd (self, "AT", error)) + return FALSE; + if (!fu_mm_device_at_cmd (self, self->detach_fastboot_at, error)) { + g_prefix_error (error, "rebooting into fastboot not supported: "); + return FALSE; + } + + /* success */ + fu_device_set_remove_delay (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_mm_device_detach (FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + g_autoptr(FuDeviceLocker) locker = NULL; + + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* This plugin supports currently two methods to download firmware: + * fastboot and qmi-pdc. A modem may require one of those, or both, + * depending on the update type or the modem type. + * + * The first time this detach() method is executed is always for a + * FuMmDevice that was created from a MM-exposed modem, which is the + * moment when we're going to decide the amount of retries we need to + * flash all firmware. + * + * If the FuMmModem is created from a MM-exposed modem and... + * a) we only support fastboot, we just trigger the fastboot detach. + * b) we only support qmi-pdc, we just exit without any detach. + * c) we support both fastboot and qmi-pdc, we will set the + * ANOTHER_WRITE_REQUIRED flag in the device and we'll trigger + * the fastboot detach. + * + * If the FuMmModem is created from udev events... + * d) it means we're in the extra required write that was flagged + * in an earlier detach(), and we need to perform the qmi-pdc + * update procedure at this time, so we just exit without any + * detach. + */ + + /* FuMmDevice created from MM... */ + if (self->omodem != NULL) { + /* both fastboot and qmi-pdc supported? another write required */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && + (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC)) { + g_debug ("both fastboot and qmi-pdc supported, so the upgrade requires another write"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + } + /* fastboot */ + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) + return fu_mm_device_detach_fastboot (device, error); + /* otherwise, assume we don't need any detach */ + return TRUE; + } + + /* FuMmDevice created from udev... + * assume we don't need any detach */ + return TRUE; +} + +typedef struct { + gchar *filename; + GBytes *bytes; + GArray *digest; + gboolean active; +} FuMmFileInfo; + +static void +fu_mm_file_info_free (FuMmFileInfo *file_info) +{ + g_clear_pointer (&file_info->digest, g_array_unref); + g_free (file_info->filename); + g_bytes_unref (file_info->bytes); + g_free (file_info); +} + +typedef struct { + FuMmDevice *device; + GError *error; + GPtrArray *file_infos; + gsize total_written; + gsize total_bytes; +} FuMmArchiveIterateCtx; + +static gboolean +fu_mm_should_be_active (const gchar *version, + const gchar *filename) +{ + g_auto(GStrv) split = NULL; + g_autofree gchar *carrier_id = NULL; + + /* The filename of the mcfg file is composed of a "mcfg." prefix, then the + * carrier code, followed by the carrier version, and finally a ".mbn" + * prefix. Here we try to guess, based on the carrier code, whether the + * specific mcfg file should be activated after the firmware upgrade + * operation. + * + * This logic requires that the previous device version includes the carrier + * code also embedded in the version string. E.g. "xxxx.VF.xxxx". If we find + * this match, we assume this is the active config to use. + */ + + split = g_strsplit (filename, ".", -1); + if (g_strv_length (split) < 4) + return FALSE; + if (g_strcmp0 (split[0], "mcfg") != 0) + return FALSE; + + carrier_id = g_strdup_printf (".%s.", split[1]); + return (g_strstr_len (version, -1, carrier_id) != NULL); +} + +static void +fu_mm_qmi_pdc_archive_iterate_mcfg (FuArchive *archive, + const gchar *filename, + GBytes *bytes, + gpointer user_data) +{ + FuMmArchiveIterateCtx *ctx = user_data; + FuMmFileInfo *file_info; + + /* filenames should be named as 'mcfg.*.mbn', e.g.: mcfg.A2.018.mbn */ + if (!g_str_has_prefix (filename, "mcfg.") || !g_str_has_suffix (filename, ".mbn")) + return; + + file_info = g_new0 (FuMmFileInfo, 1); + file_info->filename = g_strdup (filename); + file_info->bytes = g_bytes_ref (bytes); + file_info->active = fu_mm_should_be_active (fu_device_get_version (FU_DEVICE (ctx->device)), filename); + g_ptr_array_add (ctx->file_infos, file_info); + ctx->total_bytes += g_bytes_get_size (file_info->bytes); +} + +static gboolean +fu_mm_device_qmi_open (FuMmDevice *self, GError **error) +{ + self->qmi_pdc_updater = fu_qmi_pdc_updater_new (self->port_qmi); + return fu_qmi_pdc_updater_open (self->qmi_pdc_updater, error); +} + +static gboolean +fu_mm_device_qmi_close (FuMmDevice *self, GError **error) +{ + g_autoptr(FuQmiPdcUpdater) updater = NULL; + + updater = g_steal_pointer (&self->qmi_pdc_updater); + return fu_qmi_pdc_updater_close (updater, error); +} + +static gboolean +fu_mm_device_qmi_close_no_error (FuMmDevice *self, GError **error) +{ + g_autoptr(FuQmiPdcUpdater) updater = NULL; + + updater = g_steal_pointer (&self->qmi_pdc_updater); + fu_qmi_pdc_updater_close (updater, NULL); + return TRUE; +} + +static gboolean +fu_mm_device_write_firmware_qmi_pdc (FuDevice *device, GBytes *fw, GArray **active_id, GError **error) +{ + g_autoptr(FuArchive) archive = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GPtrArray) file_infos = g_ptr_array_new_with_free_func ((GDestroyNotify)fu_mm_file_info_free); + gint active_i = -1; + FuMmArchiveIterateCtx archive_context = { + .device = FU_MM_DEVICE (device), + .error = NULL, + .file_infos = file_infos, + .total_written = 0, + .total_bytes = 0, + }; + + /* decompress entire archive ahead of time */ + archive = fu_archive_new (fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + + /* boot to fastboot mode */ + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) fu_mm_device_qmi_open, + (FuDeviceLockerFunc) fu_mm_device_qmi_close, + error); + if (locker == NULL) + return FALSE; + + /* process the list of MCFG files to write */ + fu_archive_iterate (archive, fu_mm_qmi_pdc_archive_iterate_mcfg, &archive_context); + + for (guint i = 0; i < file_infos->len; i++) { + FuMmFileInfo *file_info = g_ptr_array_index (file_infos, i); + file_info->digest = fu_qmi_pdc_updater_write (archive_context.device->qmi_pdc_updater, + file_info->filename, + file_info->bytes, + &archive_context.error); + if (file_info->digest == NULL) { + g_prefix_error (&archive_context.error, + "Failed to write file '%s':", file_info->filename); + break; + } + /* if we wrongly detect more than one, just assume the latest one; this + * is not critical, it may just take a bit more time to perform the + * automatic carrier config switching in ModemManager */ + if (file_info->active) + active_i = i; + } + + /* set expected active configuration */ + if (active_i >= 0 && active_id != NULL) { + FuMmFileInfo *file_info = g_ptr_array_index (file_infos, active_i); + *active_id = g_array_ref (file_info->digest); + } + + if (archive_context.error != NULL) { + g_propagate_error (error, archive_context.error); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_mm_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(GPtrArray) array = NULL; + + /* lock device */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* qmi pdc write operation */ + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) + return fu_mm_device_write_firmware_qmi_pdc (device, fw, &self->qmi_pdc_active_id, error); + + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, + "unsupported update method"); + return FALSE; +} + +static gboolean +fu_mm_device_attach_qmi_pdc (FuMmDevice *self, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* ignore action if there is no active id specified */ + if (self->qmi_pdc_active_id == NULL) + return TRUE; + + /* errors closing may be expected if the device really reboots itself */ + locker = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_mm_device_qmi_open, + (FuDeviceLockerFunc) fu_mm_device_qmi_close_no_error, + error); + if (locker == NULL) + return FALSE; + + if (!fu_qmi_pdc_updater_activate (self->qmi_pdc_updater, self->qmi_pdc_active_id, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_mm_device_attach_noop_idle (gpointer user_data) +{ + FuMmDevice *self = FU_MM_DEVICE (user_data); + self->attach_idle = 0; + g_signal_emit (self, signals [SIGNAL_ATTACH_FINISHED], 0); + return G_SOURCE_REMOVE; +} + +static gboolean +fu_mm_device_attach_qmi_pdc_idle (gpointer user_data) +{ + FuMmDevice *self = FU_MM_DEVICE (user_data); + g_autoptr(GError) error = NULL; + + if (!fu_mm_device_attach_qmi_pdc (self, &error)) + g_warning ("qmi-pdc attach operation failed: %s", error->message); + else + g_debug ("qmi-pdc attach operation successful"); + + self->attach_idle = 0; + g_signal_emit (self, signals [SIGNAL_ATTACH_FINISHED], 0); + return G_SOURCE_REMOVE; +} + +static gboolean +fu_mm_device_attach (FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE (device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* lock device */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* we want this attach operation to be triggered asynchronously, because the engine + * must learn that it has to wait for replug before we actually trigger the reset. */ + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) + self->attach_idle = g_idle_add ((GSourceFunc) fu_mm_device_attach_qmi_pdc_idle, self); + else + self->attach_idle = g_idle_add ((GSourceFunc) fu_mm_device_attach_noop_idle, self); + + /* wait for re-probing after uninhibiting */ + fu_device_set_remove_delay (device, FU_MM_DEVICE_REMOVE_DELAY_REPROBE); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_mm_device_init (FuMmDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION); + fu_device_set_summary (FU_DEVICE (self), "Mobile broadband device"); + fu_device_add_icon (FU_DEVICE (self), "network-modem"); +} + +static void +fu_mm_device_finalize (GObject *object) +{ + FuMmDevice *self = FU_MM_DEVICE (object); + if (self->attach_idle) + g_source_remove (self->attach_idle); + if (self->qmi_pdc_active_id) + g_array_unref (self->qmi_pdc_active_id); + g_object_unref (self->manager); + if (self->omodem != NULL) + g_object_unref (self->omodem); + g_free (self->detach_fastboot_at); + g_free (self->port_at); + g_free (self->port_qmi); + g_free (self->inhibition_uid); + G_OBJECT_CLASS (fu_mm_device_parent_class)->finalize (object); +} + +static void +fu_mm_device_class_init (FuMmDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + object_class->finalize = fu_mm_device_finalize; + klass_device->to_string = fu_mm_device_to_string; + klass_device->probe = fu_mm_device_probe; + klass_device->detach = fu_mm_device_detach; + klass_device->write_firmware = fu_mm_device_write_firmware; + klass_device->attach = fu_mm_device_attach; + + signals [SIGNAL_ATTACH_FINISHED] = + g_signal_new ("attach-finished", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +FuMmDevice * +fu_mm_device_new (MMManager *manager, MMObject *omodem) +{ + FuMmDevice *self = g_object_new (FU_TYPE_MM_DEVICE, NULL); + self->manager = g_object_ref (manager); + self->omodem = g_object_ref (omodem); + self->port_at_ifnum = -1; + return self; +} + +FuPluginMmInhibitedDeviceInfo * +fu_plugin_mm_inhibited_device_info_new (FuMmDevice *device) +{ + FuPluginMmInhibitedDeviceInfo *info; + + info = g_new0 (FuPluginMmInhibitedDeviceInfo, 1); + info->physical_id = g_strdup (fu_device_get_physical_id (FU_DEVICE (device))); + info->vendor = g_strdup (fu_device_get_vendor (FU_DEVICE (device))); + info->name = g_strdup (fu_device_get_name (FU_DEVICE (device))); + info->version = g_strdup (fu_device_get_version (FU_DEVICE (device))); + info->guids = fu_device_get_guids (FU_DEVICE (device)); + info->update_methods = fu_mm_device_get_update_methods (device); + info->detach_fastboot_at = g_strdup (fu_mm_device_get_detach_fastboot_at (device)); + info->port_at_ifnum = fu_mm_device_get_port_at_ifnum (device); + info->inhibited_uid = g_strdup (fu_mm_device_get_inhibition_uid (device)); + + return info; +} + +void +fu_plugin_mm_inhibited_device_info_free (FuPluginMmInhibitedDeviceInfo *info) +{ + g_free (info->inhibited_uid); + g_free (info->physical_id); + g_free (info->vendor); + g_free (info->name); + g_free (info->version); + if (info->guids) + g_ptr_array_unref (info->guids); + g_free (info->detach_fastboot_at); + g_free (info); +} + +FuMmDevice * +fu_mm_device_udev_new (MMManager *manager, + FuPluginMmInhibitedDeviceInfo *info) +{ + FuMmDevice *self = g_object_new (FU_TYPE_MM_DEVICE, NULL); + g_debug ("creating udev-based mm device at %s", info->physical_id); + self->manager = g_object_ref (manager); + fu_device_set_physical_id (FU_DEVICE (self), info->physical_id); + fu_device_set_vendor (FU_DEVICE (self), info->vendor); + fu_device_set_name (FU_DEVICE (self), info->name); + fu_device_set_version (FU_DEVICE (self), info->version, FWUPD_VERSION_FORMAT_UNKNOWN); + self->update_methods = info->update_methods; + self->detach_fastboot_at = g_strdup (info->detach_fastboot_at); + self->port_at_ifnum = info->port_at_ifnum; + + for (guint i = 0; i < info->guids->len; i++) + fu_device_add_guid (FU_DEVICE (self), g_ptr_array_index (info->guids, i)); + + return self; +} + +void +fu_mm_device_udev_add_port (FuMmDevice *self, + const gchar *subsystem, + const gchar *path, + gint ifnum) +{ + g_return_if_fail (FU_IS_MM_DEVICE (self)); + + /* cdc-wdm ports always added unless one already set */ + if (g_str_equal (subsystem, "usbmisc") && + (self->port_qmi == NULL)) { + g_debug ("added QMI port %s (%s)", path, subsystem); + self->port_qmi = g_strdup (path); + return; + } + + if (g_str_equal (subsystem, "tty") && + (self->port_at == NULL) && + (ifnum >= 0) && (ifnum == self->port_at_ifnum)) { + g_debug ("added AT port %s (%s)", path, subsystem); + self->port_at = g_strdup (path); + return; + } + + /* otherwise, ignore all other ports */ + g_debug ("ignoring port %s (%s)", path, subsystem); +} diff -Nru fwupd-1.0.9/plugins/modem-manager/fu-mm-device.h fwupd-1.2.10/plugins/modem-manager/fu-mm-device.h --- fwupd-1.0.9/plugins/modem-manager/fu-mm-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/fu-mm-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_MM_DEVICE_H +#define __FU_MM_DEVICE_H + +#include + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_MM_DEVICE (fu_mm_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuMmDevice, fu_mm_device, FU, MM_DEVICE, FuDevice) + +FuMmDevice *fu_mm_device_new (MMManager *manager, + MMObject *omodem); +const gchar *fu_mm_device_get_inhibition_uid (FuMmDevice *device); +const gchar *fu_mm_device_get_detach_fastboot_at (FuMmDevice *device); +gint fu_mm_device_get_port_at_ifnum (FuMmDevice *device); +MMModemFirmwareUpdateMethod fu_mm_device_get_update_methods (FuMmDevice *device); + +/* support for udev-based devices */ + +typedef struct FuPluginMmInhibitedDeviceInfo FuPluginMmInhibitedDeviceInfo; +struct FuPluginMmInhibitedDeviceInfo { + gchar *inhibited_uid; + gchar *physical_id; + gchar *vendor; + gchar *name; + gchar *version; + GPtrArray *guids; + MMModemFirmwareUpdateMethod update_methods; + gchar *detach_fastboot_at; + gint port_at_ifnum; +}; +FuPluginMmInhibitedDeviceInfo *fu_plugin_mm_inhibited_device_info_new (FuMmDevice *device); +void fu_plugin_mm_inhibited_device_info_free (FuPluginMmInhibitedDeviceInfo *info); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FuPluginMmInhibitedDeviceInfo, fu_plugin_mm_inhibited_device_info_free); + +FuMmDevice *fu_mm_device_udev_new (MMManager *manager, + FuPluginMmInhibitedDeviceInfo *info); +void fu_mm_device_udev_add_port (FuMmDevice *device, + const gchar *subsystem, + const gchar *path, + gint ifnum); + +G_END_DECLS + +#endif /* __FU_MM_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/modem-manager/fu-mm-utils.c fwupd-1.2.10/plugins/modem-manager/fu-mm-utils.c --- fwupd-1.0.9/plugins/modem-manager/fu-mm-utils.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/fu-mm-utils.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include "fu-mm-utils.h" + +gboolean +fu_mm_utils_get_udev_port_info (GUdevDevice *device, + gchar **out_device_sysfs_path, + gint *out_port_ifnum, + GError **error) +{ + gint port_ifnum = -1; + const gchar *aux; + g_autoptr(GUdevDevice) parent = NULL; + g_autofree gchar *device_sysfs_path = NULL; + + /* ID_USB_INTERFACE_NUM is set on the port device itself */ + aux = g_udev_device_get_property (device, "ID_USB_INTERFACE_NUM"); + if (aux != NULL) + port_ifnum = (guint16) g_ascii_strtoull (aux, NULL, 16); + + /* we need to traverse all parents of the give udev device until we find + * the first 'usb_device' reported, which is the GUdevDevice associated with + * the full USB device (i.e. all ports of the same device). + */ + parent = g_udev_device_get_parent (device); + while (parent != NULL) { + g_autoptr(GUdevDevice) next = NULL; + + if (g_strcmp0 (g_udev_device_get_devtype (parent), "usb_device") == 0) { + device_sysfs_path = g_strdup (g_udev_device_get_sysfs_path (parent)); + break; + } + + /* check next parent */ + next = g_udev_device_get_parent (parent); + g_set_object (&parent, next); + } + + if (parent == NULL) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to lookup device info: parent usb_device not found"); + return FALSE; + } + + if (out_port_ifnum != NULL) + *out_port_ifnum = port_ifnum; + if (out_device_sysfs_path != NULL) + *out_device_sysfs_path = g_steal_pointer (&device_sysfs_path); + return TRUE; +} + +gboolean +fu_mm_utils_get_port_info (const gchar *path, + gchar **out_device_sysfs_path, + gint *out_port_ifnum, + GError **error) +{ + g_autoptr(GUdevClient) client = NULL; + g_autoptr(GUdevDevice) dev = NULL; + + client = g_udev_client_new (NULL); + dev = g_udev_client_query_by_device_file (client, path); + if (dev == NULL) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to lookup device by path"); + return FALSE; + } + + return fu_mm_utils_get_udev_port_info (dev, out_device_sysfs_path, out_port_ifnum, error); +} diff -Nru fwupd-1.0.9/plugins/modem-manager/fu-mm-utils.h fwupd-1.2.10/plugins/modem-manager/fu-mm-utils.h --- fwupd-1.0.9/plugins/modem-manager/fu-mm-utils.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/fu-mm-utils.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_MM_UTILS_H +#define __FU_MM_UTILS_H + +#include "config.h" +#include + +G_BEGIN_DECLS + +#ifndef HAVE_GUDEV_232 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevClient, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) +#pragma clang diagnostic pop +#endif + +gboolean fu_mm_utils_get_udev_port_info (GUdevDevice *dev, + gchar **device_sysfs_path, + gint *port_ifnum, + GError **error); +gboolean fu_mm_utils_get_port_info (const gchar *path, + gchar **device_sysfs_path, + gint *port_ifnum, + GError **error); + +G_END_DECLS + +#endif /* __FU_MM_UTILS_H */ diff -Nru fwupd-1.0.9/plugins/modem-manager/fu-plugin-modem-manager.c fwupd-1.2.10/plugins/modem-manager/fu-plugin-modem-manager.c --- fwupd-1.0.9/plugins/modem-manager/fu-plugin-modem-manager.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/fu-plugin-modem-manager.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-plugin-vfuncs.h" + +#include "fu-mm-device.h" +#include "fu-mm-utils.h" + +/* amount of time to wait for ports of the same device being exposed by kernel */ +#define FU_MM_UDEV_DEVICE_PORTS_TIMEOUT 3 /* s */ + +typedef struct FuPluginMmInhibitedDeviceInfo FuPluginMmInhibitedDeviceInfo; + +struct FuPluginData { + MMManager *manager; + gboolean manager_ready; + GUdevClient *udev_client; + guint udev_timeout_id; + + /* when a device is inhibited from MM, we store all relevant details + * ourselves to recreate a functional device object even without MM + */ + FuPluginMmInhibitedDeviceInfo *inhibited; +}; + +static void +fu_plugin_mm_udev_device_removed (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + FuMmDevice *dev; + + if (priv->inhibited == NULL) + return; + + dev = fu_plugin_cache_lookup (plugin, priv->inhibited->physical_id); + if (dev == NULL) + return; + + /* once the first port is gone, consider device is gone */ + fu_plugin_cache_remove (plugin, priv->inhibited->physical_id); + fu_plugin_device_remove (plugin, FU_DEVICE (dev)); + + /* no need to wait for more ports, cancel that right away */ + if (priv->udev_timeout_id != 0) { + g_source_remove (priv->udev_timeout_id); + priv->udev_timeout_id = 0; + } +} + +static void +fu_plugin_mm_uninhibit_device (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FuPluginMmInhibitedDeviceInfo) info = NULL; + + /* get the device removed from the plugin cache before uninhibiting */ + fu_plugin_mm_udev_device_removed (plugin); + + info = g_steal_pointer (&priv->inhibited); + if ((priv->manager != NULL) && (info != NULL)) { + g_debug ("uninhibit modemmanager device with uid %s", info->inhibited_uid); + mm_manager_uninhibit_device_sync (priv->manager, info->inhibited_uid, NULL, NULL); + } +} + +static gboolean +fu_plugin_mm_udev_device_ports_timeout (gpointer user_data) +{ + FuPlugin *plugin = user_data; + FuPluginData *priv = fu_plugin_get_data (plugin); + FuMmDevice *dev; + g_autoptr(GError) error = NULL; + + g_return_val_if_fail (priv->inhibited != NULL, G_SOURCE_REMOVE); + priv->udev_timeout_id = 0; + + dev = fu_plugin_cache_lookup (plugin, priv->inhibited->physical_id); + if (dev != NULL) { + if (!fu_device_probe (FU_DEVICE (dev), &error)) { + g_warning ("failed to probe MM device: %s", error->message); + } else { + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + } + } + + return G_SOURCE_REMOVE; +} + +static void +fu_plugin_mm_udev_device_ports_timeout_reset (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + + g_return_if_fail (priv->inhibited != NULL); + if (priv->udev_timeout_id != 0) + g_source_remove (priv->udev_timeout_id); + priv->udev_timeout_id = g_timeout_add_seconds (FU_MM_UDEV_DEVICE_PORTS_TIMEOUT, + fu_plugin_mm_udev_device_ports_timeout, + plugin); +} + +static void +fu_plugin_mm_udev_device_port_added (FuPlugin *plugin, + const gchar *subsystem, + const gchar *path, + gint ifnum) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + FuMmDevice *existing; + g_autoptr(FuMmDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + g_return_if_fail (priv->inhibited != NULL); + existing = fu_plugin_cache_lookup (plugin, priv->inhibited->physical_id); + if (existing != NULL) { + /* add port to existing device */ + fu_mm_device_udev_add_port (existing, subsystem, path, ifnum); + fu_plugin_mm_udev_device_ports_timeout_reset (plugin); + return; + } + /* create device and add to cache */ + dev = fu_mm_device_udev_new (priv->manager, priv->inhibited); + fu_mm_device_udev_add_port (dev, subsystem, path, ifnum); + fu_plugin_cache_add (plugin, priv->inhibited->physical_id, dev); + + /* wait a bit before probing, in case more ports get added */ + fu_plugin_mm_udev_device_ports_timeout_reset (plugin); +} + +static gboolean +fu_plugin_mm_udev_uevent_cb (GUdevClient *udev, + const gchar *action, + GUdevDevice *device, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN (user_data); + FuPluginData *priv = fu_plugin_get_data (plugin); + const gchar *subsystem = g_udev_device_get_subsystem (device); + const gchar *name = g_udev_device_get_name (device); + g_autofree gchar *path = NULL; + g_autofree gchar *device_sysfs_path = NULL; + gint ifnum = -1; + + if (action == NULL || subsystem == NULL || priv->inhibited == NULL || name == NULL) + return TRUE; + + /* ignore if loading port info fails */ + if (!fu_mm_utils_get_udev_port_info (device, &device_sysfs_path, &ifnum, NULL)) + return TRUE; + + /* ignore all events for ports not owned by our device */ + if (g_strcmp0 (device_sysfs_path, priv->inhibited->physical_id) != 0) + return TRUE; + + /* ignore non-cdc-wdm usbmisc ports */ + if (g_str_equal (subsystem, "usbmisc") && !g_str_has_prefix (name, "cdc-wdm")) + return TRUE; + + path = g_strdup_printf ("/dev/%s", name); + + if ((g_str_equal (action, "add")) || (g_str_equal (action, "change"))) { + g_debug ("added port to inhibited modem: %s (ifnum %d)", path, ifnum); + fu_plugin_mm_udev_device_port_added (plugin, subsystem, path, ifnum); + } else if (g_str_equal (action, "remove")) { + g_debug ("removed port from inhibited modem: %s", path); + fu_plugin_mm_udev_device_removed (plugin); + } + + return TRUE; +} + +static gboolean +fu_plugin_mm_inhibit_device (FuPlugin *plugin, FuDevice *device, GError **error) +{ + static const gchar *subsystems[] = { "tty", "usbmisc", NULL }; + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FuPluginMmInhibitedDeviceInfo) info = NULL; + + fu_plugin_mm_uninhibit_device (plugin); + + info = fu_plugin_mm_inhibited_device_info_new (FU_MM_DEVICE (device)); + + g_debug ("inhibit modemmanager device with uid %s", info->inhibited_uid); + if (!mm_manager_inhibit_device_sync (priv->manager, info->inhibited_uid, NULL, error)) + return FALSE; + + /* setup inhibited device info */ + priv->inhibited = g_steal_pointer (&info); + + /* as soon as inhibition is place, we need to do modem device monitoring based + * on the udev client, as MM no longer reports devices */ + priv->udev_client = g_udev_client_new (subsystems); + g_signal_connect (priv->udev_client, "uevent", + G_CALLBACK (fu_plugin_mm_udev_uevent_cb), plugin); + + return TRUE; +} + +static void +fu_plugin_mm_device_add (FuPlugin *plugin, MMObject *modem) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + const gchar *object_path = mm_object_get_path (modem); + g_autoptr(FuMmDevice) dev = NULL; + g_autoptr(GError) error = NULL; + if (fu_plugin_cache_lookup (plugin, object_path) != NULL) { + g_warning ("MM device already added, ignoring"); + return; + } + dev = fu_mm_device_new (priv->manager, modem); + if (!fu_device_probe (FU_DEVICE (dev), &error)) { + g_warning ("failed to probe MM device: %s", error->message); + return; + } + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + fu_plugin_cache_add (plugin, object_path, dev); +} + +static void +fu_plugin_mm_device_added_cb (MMManager *manager, MMObject *modem, FuPlugin *plugin) +{ + fu_plugin_mm_device_add (plugin, modem); +} + +static void +fu_plugin_mm_device_removed_cb (MMManager *manager, MMObject *modem, FuPlugin *plugin) +{ + const gchar *object_path = mm_object_get_path (modem); + FuMmDevice *dev = fu_plugin_cache_lookup (plugin, object_path); + if (dev == NULL) + return; + g_debug ("removed modem: %s", mm_object_get_path (modem)); + fu_plugin_cache_remove (plugin, object_path); + fu_plugin_device_remove (plugin, FU_DEVICE (dev)); +} + +static void +fu_plugin_mm_teardown_manager (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + + if (priv->manager_ready) { + g_debug ("ModemManager no longer available"); + g_signal_handlers_disconnect_by_func (priv->manager, + G_CALLBACK (fu_plugin_mm_device_added_cb), + plugin); + g_signal_handlers_disconnect_by_func (priv->manager, + G_CALLBACK (fu_plugin_mm_device_removed_cb), + plugin); + priv->manager_ready = FALSE; + } +} + +static void +fu_plugin_mm_setup_manager (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + const gchar *version = mm_manager_get_version (priv->manager); + GList *list; + + if (fu_common_vercmp (version, MM_REQUIRED_VERSION) < 0) { + g_warning ("ModemManager %s is available, but need at least %s", + version, MM_REQUIRED_VERSION); + return; + } + + g_debug ("ModemManager %s is available", version); + + g_signal_connect (priv->manager, "object-added", + G_CALLBACK (fu_plugin_mm_device_added_cb), plugin); + g_signal_connect (priv->manager, "object-removed", + G_CALLBACK (fu_plugin_mm_device_removed_cb), plugin); + + list = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (priv->manager)); + for (GList *l = list; l != NULL; l = g_list_next (l)) { + MMObject *modem = MM_OBJECT (l->data); + fu_plugin_mm_device_add (plugin, modem); + g_object_unref (modem); + } + g_list_free (list); + + priv->manager_ready = TRUE; +} + +static void +fu_plugin_mm_name_owner_updated (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + const gchar *name_owner; + name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->manager)); + if (name_owner != NULL) + fu_plugin_mm_setup_manager (plugin); + else + fu_plugin_mm_teardown_manager (plugin); +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_signal_connect_swapped (priv->manager, "notify::name-owner", + G_CALLBACK (fu_plugin_mm_name_owner_updated), + plugin); + fu_plugin_mm_name_owner_updated (plugin); + return TRUE; +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(GDBusConnection) connection = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) + return FALSE; + priv->manager = mm_manager_new_sync (connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + NULL, error); + if (priv->manager == NULL) + return FALSE; + + return TRUE; +} + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); +} + +void +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + + fu_plugin_mm_uninhibit_device (plugin); + + if (priv->udev_timeout_id) + g_source_remove (priv->udev_timeout_id); + if (priv->udev_client) + g_object_unref (priv->udev_client); + if (priv->manager != NULL) + g_object_unref (priv->manager); +} + +gboolean +fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* inhibit device and track it inside the plugin, not bound to the + * lifetime of the FuMmDevice, because that object will only exist for + * as long as the ModemManager device exists, and inhibiting will + * implicitly remove the device from ModemManager. */ + if (priv->inhibited == NULL) { + if (!fu_plugin_mm_inhibit_device (plugin, device, error)) + return FALSE; + } + + /* reset */ + if (!fu_device_detach (device, error)) { + fu_plugin_mm_uninhibit_device (plugin); + return FALSE; + } + + /* note: wait for replug set by device if it really needs it */ + return TRUE; +} + +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, flags, error); +} + +static void +fu_plugin_mm_device_attach_finished (gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN (user_data); + fu_plugin_mm_uninhibit_device (plugin); +} + +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; + + /* schedule device attach asynchronously, which is extremely important + * so that engine can setup the device "waiting" logic before the actual + * attach procedure happens (which will reset the module if it worked + * properly) */ + if (!fu_device_attach (device, error)) + return FALSE; + + /* this signal will always be emitted asynchronously */ + g_signal_connect_swapped (device, "attach-finished", + G_CALLBACK (fu_plugin_mm_device_attach_finished), plugin); + + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/modem-manager/fu-qmi-pdc-updater.c fwupd-1.2.10/plugins/modem-manager/fu-qmi-pdc-updater.c --- fwupd-1.0.9/plugins/modem-manager/fu-qmi-pdc-updater.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/fu-qmi-pdc-updater.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-qmi-pdc-updater.h" + +#define FU_QMI_PDC_MAX_OPEN_ATTEMPTS 8 + +struct _FuQmiPdcUpdater { + GObject parent_instance; + gchar *qmi_port; + QmiDevice *qmi_device; + QmiClientPdc *qmi_client; +}; + +G_DEFINE_TYPE (FuQmiPdcUpdater, fu_qmi_pdc_updater, G_TYPE_OBJECT) + +typedef struct { + GMainLoop *mainloop; + QmiDevice *qmi_device; + QmiClientPdc *qmi_client; + GError *error; + guint open_attempts; +} OpenContext; + +static void fu_qmi_pdc_updater_qmi_device_open_attempt (OpenContext *ctx); + +static void +fu_qmi_pdc_updater_qmi_device_open_abort_ready (GObject *qmi_device, GAsyncResult *res, gpointer user_data) +{ + OpenContext *ctx = (OpenContext *) user_data; + + g_warn_if_fail (ctx->error != NULL); + + /* ignore errors when aborting open */ + qmi_device_close_finish (QMI_DEVICE (qmi_device), res, NULL); + + ctx->open_attempts--; + if (ctx->open_attempts == 0) { + g_clear_object (&ctx->qmi_client); + g_clear_object (&ctx->qmi_device); + g_main_loop_quit (ctx->mainloop); + return; + } + + /* retry */ + g_clear_error (&ctx->error); + fu_qmi_pdc_updater_qmi_device_open_attempt (ctx); +} + +static void +fu_qmi_pdc_updater_open_abort (OpenContext *ctx) +{ + qmi_device_close_async (ctx->qmi_device, + 15, NULL, fu_qmi_pdc_updater_qmi_device_open_abort_ready, ctx); +} + +static void +fu_qmi_pdc_updater_qmi_device_allocate_client_ready (GObject *qmi_device, GAsyncResult *res, gpointer user_data) +{ + OpenContext *ctx = (OpenContext *) user_data; + + ctx->qmi_client = QMI_CLIENT_PDC (qmi_device_allocate_client_finish (QMI_DEVICE (qmi_device), res, &ctx->error)); + if (ctx->qmi_client == NULL) { + fu_qmi_pdc_updater_open_abort (ctx); + return; + } + + g_main_loop_quit (ctx->mainloop); +} + +static void +fu_qmi_pdc_updater_qmi_device_open_ready (GObject *qmi_device, GAsyncResult *res, gpointer user_data) +{ + OpenContext *ctx = (OpenContext *) user_data; + + if (!qmi_device_open_finish (QMI_DEVICE (qmi_device), res, &ctx->error)) { + fu_qmi_pdc_updater_open_abort (ctx); + return; + } + + qmi_device_allocate_client (ctx->qmi_device, QMI_SERVICE_PDC, QMI_CID_NONE, 5, NULL, + fu_qmi_pdc_updater_qmi_device_allocate_client_ready, ctx); +} + +static void +fu_qmi_pdc_updater_qmi_device_open_attempt (OpenContext *ctx) +{ + QmiDeviceOpenFlags open_flags = QMI_DEVICE_OPEN_FLAGS_NONE; + + /* automatically detect QMI and MBIM ports */ + open_flags |= QMI_DEVICE_OPEN_FLAGS_AUTO; + /* qmi pdc requires indications, so enable them by default */ + open_flags |= QMI_DEVICE_OPEN_FLAGS_EXPECT_INDICATIONS; + /* all communication through the proxy */ + open_flags |= QMI_DEVICE_OPEN_FLAGS_PROXY; + + g_debug ("trying to open QMI device..."); + qmi_device_open (ctx->qmi_device, open_flags, 5, NULL, + fu_qmi_pdc_updater_qmi_device_open_ready, ctx); +} + +static void +fu_qmi_pdc_updater_qmi_device_new_ready (GObject *source, GAsyncResult *res, gpointer user_data) +{ + OpenContext *ctx = (OpenContext *) user_data; + + ctx->qmi_device = qmi_device_new_finish (res, &ctx->error); + if (ctx->qmi_device == NULL) { + g_main_loop_quit (ctx->mainloop); + return; + } + + fu_qmi_pdc_updater_qmi_device_open_attempt (ctx); +} + +gboolean +fu_qmi_pdc_updater_open (FuQmiPdcUpdater *self, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new (NULL, FALSE); + g_autoptr(GFile) qmi_device_file = g_file_new_for_path (self->qmi_port); + OpenContext ctx = { + .mainloop = mainloop, + .qmi_device = NULL, + .qmi_client = NULL, + .error = NULL, + .open_attempts = FU_QMI_PDC_MAX_OPEN_ATTEMPTS, + }; + + qmi_device_new (qmi_device_file, NULL, fu_qmi_pdc_updater_qmi_device_new_ready, &ctx); + g_main_loop_run (mainloop); + + /* either we have all device, client and config list set, or otherwise error is set */ + + if ((ctx.qmi_device != NULL) && (ctx.qmi_client != NULL)) { + g_warn_if_fail (!ctx.error); + self->qmi_device = ctx.qmi_device; + self->qmi_client = ctx.qmi_client; + /* success */ + return TRUE; + } + + g_warn_if_fail (ctx.error != NULL); + g_warn_if_fail (ctx.qmi_device == NULL); + g_warn_if_fail (ctx.qmi_client == NULL); + g_propagate_error (error, ctx.error); + return FALSE; +} + +typedef struct { + GMainLoop *mainloop; + QmiDevice *qmi_device; + QmiClientPdc *qmi_client; + GError *error; +} CloseContext; + +static void +fu_qmi_pdc_updater_qmi_device_close_ready (GObject *qmi_device, GAsyncResult *res, gpointer user_data) +{ + CloseContext *ctx = (CloseContext *) user_data; + + /* ignore errors when closing if we had one already set when releasing client */ + qmi_device_close_finish (QMI_DEVICE (qmi_device), res, (ctx->error == NULL) ? &ctx->error : NULL); + g_clear_object (&ctx->qmi_device); + g_main_loop_quit (ctx->mainloop); +} + +static void +fu_qmi_pdc_updater_qmi_device_release_client_ready (GObject *qmi_device, GAsyncResult *res, gpointer user_data) +{ + CloseContext *ctx = (CloseContext *) user_data; + + qmi_device_release_client_finish (QMI_DEVICE (qmi_device), res, &ctx->error); + g_clear_object (&ctx->qmi_client); + + qmi_device_close_async (ctx->qmi_device, + 15, NULL, fu_qmi_pdc_updater_qmi_device_close_ready, ctx); +} + +gboolean +fu_qmi_pdc_updater_close (FuQmiPdcUpdater *self, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new (NULL, FALSE); + CloseContext ctx = { + .mainloop = mainloop, + .qmi_device = g_steal_pointer (&self->qmi_device), + .qmi_client = g_steal_pointer (&self->qmi_client), + }; + + qmi_device_release_client (ctx.qmi_device, QMI_CLIENT (ctx.qmi_client), + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, + 5, NULL, fu_qmi_pdc_updater_qmi_device_release_client_ready, &ctx); + g_main_loop_run (mainloop); + + /* we should always have both device and client cleared, and optionally error set */ + + g_warn_if_fail (ctx.qmi_device == NULL); + g_warn_if_fail (ctx.qmi_client == NULL); + + if (ctx.error != NULL) { + g_propagate_error (error, ctx.error); + return FALSE; + } + + return TRUE; +} + +#define QMI_LOAD_CHUNK_SIZE 0x400 + +typedef struct { + GMainLoop *mainloop; + QmiClientPdc *qmi_client; + GError *error; + gulong indication_id; + guint timeout_id; + GBytes *blob; + GArray *digest; + gsize offset; + guint token; +} WriteContext; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcLoadConfigInput, qmi_message_pdc_load_config_input_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcLoadConfigOutput, qmi_message_pdc_load_config_output_unref) +#pragma clang diagnostic pop + +static void fu_qmi_pdc_updater_load_config (WriteContext *ctx); + +static gboolean +fu_qmi_pdc_updater_load_config_timeout (gpointer user_data) +{ + WriteContext *ctx = user_data; + + ctx->timeout_id = 0; + g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + g_set_error_literal (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED, + "couldn't load mcfg: timed out"); + g_main_loop_quit (ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static void +fu_qmi_pdc_updater_load_config_indication (QmiClientPdc *client, + QmiIndicationPdcLoadConfigOutput *output, + WriteContext *ctx) +{ + gboolean frame_reset; + guint32 remaining_size; + guint16 error_code = 0; + + g_source_remove (ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + if (!qmi_indication_pdc_load_config_output_get_indication_result (output, &error_code, &ctx->error)) { + g_main_loop_quit (ctx->mainloop); + return; + } + + if (error_code != 0) { + /* when a given mcfg file already exists in the device, an "invalid id" error is returned; + * the error naming here is a bit off, as the same protocol error number is used both for + * 'invalid id' and 'invalid qos id' + */ + if (error_code == QMI_PROTOCOL_ERROR_INVALID_QOS_ID) { + g_debug ("file already available in device"); + g_main_loop_quit (ctx->mainloop); + return; + } + + g_set_error (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED, + "couldn't load mcfg: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + g_main_loop_quit (ctx->mainloop); + return; + } + + if (qmi_indication_pdc_load_config_output_get_frame_reset (output, &frame_reset, NULL) && frame_reset) { + g_set_error (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED, + "couldn't load mcfg: sent data discarded"); + g_main_loop_quit (ctx->mainloop); + return; + } + + if (!qmi_indication_pdc_load_config_output_get_remaining_size (output, &remaining_size, &ctx->error)) { + g_prefix_error (&ctx->error, "couldn't load remaining size: "); + g_main_loop_quit (ctx->mainloop); + return; + } + + if (remaining_size == 0) { + g_debug ("finished loading mcfg"); + g_main_loop_quit (ctx->mainloop); + return; + } + + g_debug ("loading next chunk (%u bytes remaining)", remaining_size); + fu_qmi_pdc_updater_load_config (ctx); +} + +static void +fu_qmi_pdc_updater_load_config_ready (GObject *qmi_client, GAsyncResult *res, gpointer user_data) +{ + WriteContext *ctx = (WriteContext *) user_data; + g_autoptr(QmiMessagePdcLoadConfigOutput) output = NULL; + + output = qmi_client_pdc_load_config_finish (QMI_CLIENT_PDC (qmi_client), res, &ctx->error); + if (output == NULL) { + g_main_loop_quit (ctx->mainloop); + return; + } + + if (!qmi_message_pdc_load_config_output_get_result (output, &ctx->error)) { + g_main_loop_quit (ctx->mainloop); + return; + } + + /* after receiving the response to our request, we now expect an indication + * with the actual result of the operation */ + g_warn_if_fail (ctx->indication_id == 0); + ctx->indication_id = g_signal_connect (ctx->qmi_client, "load-config", + G_CALLBACK (fu_qmi_pdc_updater_load_config_indication), ctx); + + /* don't wait forever */ + g_warn_if_fail (ctx->timeout_id == 0); + ctx->timeout_id = g_timeout_add_seconds (5, fu_qmi_pdc_updater_load_config_timeout, ctx); +} + +static void +fu_qmi_pdc_updater_load_config (WriteContext *ctx) +{ + g_autoptr(QmiMessagePdcLoadConfigInput) input = NULL; + g_autoptr(GArray) chunk = NULL; + gsize full_size; + gsize chunk_size; + + input = qmi_message_pdc_load_config_input_new (); + qmi_message_pdc_load_config_input_set_token (input, ctx->token++, NULL); + + full_size = g_bytes_get_size (ctx->blob); + if ((ctx->offset + QMI_LOAD_CHUNK_SIZE) > full_size) + chunk_size = full_size - ctx->offset; + else + chunk_size = QMI_LOAD_CHUNK_SIZE; + + chunk = g_array_sized_new (FALSE, FALSE, sizeof (guint8), chunk_size); + g_array_set_size (chunk, chunk_size); + memcpy (chunk->data, (const guint8 *)g_bytes_get_data (ctx->blob, NULL) + ctx->offset, chunk_size); + + qmi_message_pdc_load_config_input_set_config_chunk (input, + QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, + ctx->digest, + full_size, + chunk, + NULL); + ctx->offset += chunk_size; + + qmi_client_pdc_load_config (ctx->qmi_client, input, 10, NULL, + fu_qmi_pdc_updater_load_config_ready, ctx); +} + +static GArray * +fu_qmi_pdc_updater_get_checksum (GBytes *blob) +{ + gsize file_size; + gsize hash_size; + GArray *digest; + g_autoptr(GChecksum) checksum = NULL; + + /* get checksum, to be used as unique id */ + file_size = g_bytes_get_size (blob); + hash_size = g_checksum_type_get_length (G_CHECKSUM_SHA1); + checksum = g_checksum_new (G_CHECKSUM_SHA1); + g_checksum_update (checksum, g_bytes_get_data (blob, NULL), file_size); + /* libqmi expects a GArray of bytes, not a GByteArray */ + digest = g_array_sized_new (FALSE, FALSE, sizeof (guint8), hash_size); + g_array_set_size (digest, hash_size); + g_checksum_get_digest (checksum, (guint8 *)digest->data, &hash_size); + + return digest; +} + +GArray * +fu_qmi_pdc_updater_write (FuQmiPdcUpdater *self, const gchar *filename, GBytes *blob, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new (NULL, FALSE); + g_autoptr(GArray) digest = fu_qmi_pdc_updater_get_checksum (blob); + WriteContext ctx = { + .mainloop = mainloop, + .qmi_client = self->qmi_client, + .error = NULL, + .indication_id = 0, + .timeout_id = 0, + .blob = blob, + .digest = digest, + .offset = 0, + .token = 0, + }; + + fu_qmi_pdc_updater_load_config (&ctx); + g_main_loop_run (mainloop); + + if (ctx.error != NULL) { + g_propagate_error (error, ctx.error); + return NULL; + } + + return g_steal_pointer (&digest); +} + +typedef struct { + GMainLoop *mainloop; + QmiClientPdc *qmi_client; + GError *error; + gulong indication_id; + guint timeout_id; + GArray *digest; + guint token; +} ActivateContext; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcActivateConfigInput, qmi_message_pdc_activate_config_input_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcActivateConfigOutput, qmi_message_pdc_activate_config_output_unref) +#pragma clang diagnostic pop + +static gboolean +fu_qmi_pdc_updater_activate_config_timeout (gpointer user_data) +{ + ActivateContext *ctx = user_data; + + ctx->timeout_id = 0; + g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + /* not an error, the device may go away without sending the indication */ + g_main_loop_quit (ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static void +fu_qmi_pdc_updater_activate_config_indication (QmiClientPdc *client, + QmiIndicationPdcActivateConfigOutput *output, + ActivateContext *ctx) +{ + guint16 error_code = 0; + + g_source_remove (ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + if (!qmi_indication_pdc_activate_config_output_get_indication_result (output, &error_code, &ctx->error)) { + g_main_loop_quit (ctx->mainloop); + return; + } + + if (error_code != 0) { + g_set_error (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED, + "couldn't activate config: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + g_main_loop_quit (ctx->mainloop); + return; + } + + /* assume ok */ + g_debug ("successful activate configuration indication: assuming device reset is ongoing"); + g_main_loop_quit (ctx->mainloop); +} + +static void +fu_qmi_pdc_updater_activate_config_ready (GObject *qmi_client, GAsyncResult *res, gpointer user_data) +{ + ActivateContext *ctx = (ActivateContext *) user_data; + g_autoptr(QmiMessagePdcActivateConfigOutput) output = NULL; + + output = qmi_client_pdc_activate_config_finish (QMI_CLIENT_PDC (qmi_client), res, &ctx->error); + if (output == NULL) { + /* If we didn't receive a response, this is a good indication that the device + * reseted itself, we can consider this a successful operation. + * Note: not using g_error_matches() to avoid matching the domain, because the + * error may be either QMI_CORE_ERROR_TIMEOUT or MBIM_CORE_ERROR_TIMEOUT (same + * numeric value), and we don't want to build-depend on libmbim just for this. + */ + if (ctx->error->code == QMI_CORE_ERROR_TIMEOUT) { + g_debug ("request to activate configuration timed out: assuming device reset is ongoing"); + g_clear_error (&ctx->error); + } + g_main_loop_quit (ctx->mainloop); + return; + } + + if (!qmi_message_pdc_activate_config_output_get_result (output, &ctx->error)) { + g_main_loop_quit (ctx->mainloop); + return; + } + + /* When we activate the config, if the operation is successful, we'll just + * see the modem going away completely. So, do not consider an error the timeout + * waiting for the Activate Config indication, as that is actually a good + * thing. + */ + g_warn_if_fail (ctx->indication_id == 0); + ctx->indication_id = g_signal_connect (ctx->qmi_client, "activate-config", + G_CALLBACK (fu_qmi_pdc_updater_activate_config_indication), ctx); + + /* don't wait forever */ + g_warn_if_fail (ctx->timeout_id == 0); + ctx->timeout_id = g_timeout_add_seconds (5, fu_qmi_pdc_updater_activate_config_timeout, ctx); +} + +static void +fu_qmi_pdc_updater_activate_config (ActivateContext *ctx) +{ + g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL; + + input = qmi_message_pdc_activate_config_input_new (); + qmi_message_pdc_activate_config_input_set_config_type (input, QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, NULL); + qmi_message_pdc_activate_config_input_set_token (input, ctx->token++, NULL); + + g_debug ("activating selected configuration..."); + qmi_client_pdc_activate_config (ctx->qmi_client, input, 5, NULL, + fu_qmi_pdc_updater_activate_config_ready, ctx); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcSetSelectedConfigInput, qmi_message_pdc_set_selected_config_input_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcSetSelectedConfigOutput, qmi_message_pdc_set_selected_config_output_unref) +#pragma clang diagnostic pop + +static gboolean +fu_qmi_pdc_updater_set_selected_config_timeout (gpointer user_data) +{ + ActivateContext *ctx = user_data; + + ctx->timeout_id = 0; + g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + g_set_error_literal (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED, + "couldn't set selected config: timed out"); + g_main_loop_quit (ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static void +fu_qmi_pdc_updater_set_selected_config_indication (QmiClientPdc *client, + QmiIndicationPdcSetSelectedConfigOutput *output, + ActivateContext *ctx) +{ + guint16 error_code = 0; + + g_source_remove (ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + if (!qmi_indication_pdc_set_selected_config_output_get_indication_result (output, &error_code, &ctx->error)) { + g_main_loop_quit (ctx->mainloop); + return; + } + + if (error_code != 0) { + g_set_error (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED, + "couldn't set selected config: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code)); + g_main_loop_quit (ctx->mainloop); + return; + } + + g_debug ("current configuration successfully selected..."); + + /* now activate config */ + fu_qmi_pdc_updater_activate_config (ctx); +} + +static void +fu_qmi_pdc_updater_set_selected_config_ready (GObject *qmi_client, GAsyncResult *res, gpointer user_data) +{ + ActivateContext *ctx = (ActivateContext *) user_data; + g_autoptr(QmiMessagePdcSetSelectedConfigOutput) output = NULL; + + output = qmi_client_pdc_set_selected_config_finish (QMI_CLIENT_PDC (qmi_client), res, &ctx->error); + if (output == NULL) { + g_main_loop_quit (ctx->mainloop); + return; + } + + if (!qmi_message_pdc_set_selected_config_output_get_result (output, &ctx->error)) { + g_main_loop_quit (ctx->mainloop); + return; + } + + /* after receiving the response to our request, we now expect an indication + * with the actual result of the operation */ + g_warn_if_fail (ctx->indication_id == 0); + ctx->indication_id = g_signal_connect (ctx->qmi_client, "set-selected-config", + G_CALLBACK (fu_qmi_pdc_updater_set_selected_config_indication), ctx); + + /* don't wait forever */ + g_warn_if_fail (ctx->timeout_id == 0); + ctx->timeout_id = g_timeout_add_seconds (5, fu_qmi_pdc_updater_set_selected_config_timeout, ctx); +} + +static void +fu_qmi_pdc_updater_set_selected_config (ActivateContext *ctx) +{ + g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL; + QmiConfigTypeAndId type_and_id; + + type_and_id.config_type = QMI_PDC_CONFIGURATION_TYPE_SOFTWARE; + type_and_id.id = ctx->digest; + + input = qmi_message_pdc_set_selected_config_input_new (); + qmi_message_pdc_set_selected_config_input_set_type_with_id (input, &type_and_id, NULL); + qmi_message_pdc_set_selected_config_input_set_token (input, ctx->token++, NULL); + + g_debug ("selecting current configuration..."); + qmi_client_pdc_set_selected_config (ctx->qmi_client, input, 10, NULL, + fu_qmi_pdc_updater_set_selected_config_ready, ctx); +} + +gboolean +fu_qmi_pdc_updater_activate (FuQmiPdcUpdater *self, GArray *digest, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new (NULL, FALSE); + ActivateContext ctx = { + .mainloop = mainloop, + .qmi_client = self->qmi_client, + .error = NULL, + .indication_id = 0, + .timeout_id = 0, + .digest = digest, + .token = 0, + }; + + fu_qmi_pdc_updater_set_selected_config (&ctx); + g_main_loop_run (mainloop); + + if (ctx.error != NULL) { + g_propagate_error (error, ctx.error); + return FALSE; + } + + return TRUE; +} + +static void +fu_qmi_pdc_updater_init (FuQmiPdcUpdater *self) +{ +} + +static void +fu_qmi_pdc_updater_finalize (GObject *object) +{ + FuQmiPdcUpdater *self = FU_QMI_PDC_UPDATER (object); + g_warn_if_fail (self->qmi_client == NULL); + g_warn_if_fail (self->qmi_device == NULL); + g_free (self->qmi_port); + G_OBJECT_CLASS (fu_qmi_pdc_updater_parent_class)->finalize (object); +} + +static void +fu_qmi_pdc_updater_class_init (FuQmiPdcUpdaterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_qmi_pdc_updater_finalize; +} + +FuQmiPdcUpdater * +fu_qmi_pdc_updater_new (const gchar *path) +{ + FuQmiPdcUpdater *self = g_object_new (FU_TYPE_QMI_PDC_UPDATER, NULL); + self->qmi_port = g_strdup (path); + return self; +} diff -Nru fwupd-1.0.9/plugins/modem-manager/fu-qmi-pdc-updater.h fwupd-1.2.10/plugins/modem-manager/fu-qmi-pdc-updater.h --- fwupd-1.0.9/plugins/modem-manager/fu-qmi-pdc-updater.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/fu-qmi-pdc-updater.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_QMI_PDC_UPDATER_H +#define __FU_QMI_PDC_UPDATER_H + +#include + +G_BEGIN_DECLS + +#define FU_TYPE_QMI_PDC_UPDATER (fu_qmi_pdc_updater_get_type ()) +G_DECLARE_FINAL_TYPE (FuQmiPdcUpdater, fu_qmi_pdc_updater, FU, QMI_PDC_UPDATER, GObject) + +FuQmiPdcUpdater *fu_qmi_pdc_updater_new (const gchar *qmi_port); +gboolean fu_qmi_pdc_updater_open (FuQmiPdcUpdater *self, + GError **error); +GArray *fu_qmi_pdc_updater_write (FuQmiPdcUpdater *self, + const gchar *filename, + GBytes *blob, + GError **error); +gboolean fu_qmi_pdc_updater_activate (FuQmiPdcUpdater *self, + GArray *digest, + GError **error); +gboolean fu_qmi_pdc_updater_close (FuQmiPdcUpdater *self, + GError **error); + +G_END_DECLS + +#endif /* __FU_QMI_PDC_UPDATER_H */ diff -Nru fwupd-1.0.9/plugins/modem-manager/meson.build fwupd-1.2.10/plugins/modem-manager/meson.build --- fwupd-1.0.9/plugins/modem-manager/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,29 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginMm"'] + +shared_module('fu_plugin_modem_manager', + fu_hash, + sources : [ + 'fu-plugin-modem-manager.c', + 'fu-mm-device.c', + 'fu-qmi-pdc-updater.c', + 'fu-mm-utils.c' + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + c_args : [ + cargs, + ], + link_with : [ + libfwupdprivate, + ], + dependencies : [ + plugin_deps, + libmm_glib, + libqmi_glib, + ], +) diff -Nru fwupd-1.0.9/plugins/modem-manager/README.md fwupd-1.2.10/plugins/modem-manager/README.md --- fwupd-1.0.9/plugins/modem-manager/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/modem-manager/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,42 @@ +ModemManager +============ + +Introduction +------------ + +This plugin adds support for devices managed by ModemManager. + +GUID Generation +--------------- + +These device use the ModemManager "Firmware Device IDs" as the GUID, e.g. + + * `USB\VID_413C&PID_81D7&REV_0318&CARRIER_VODAFONE` + * `USB\VID_413C&PID_81D7&REV_0318` + * `USB\VID_413C&PID_81D7` + * `USB\VID_413C` + +Update method: fastboot +----------------------- + +If the device supports the 'fastboot' update method, it must also report which +AT command should be used to trigger the modem reboot into fastboot mode. + +Once the device is in fastboot mode, the firmware upgrade process will happen +as defined e.g. in the 'flashfile.xml' file. Every file included in the CAB that +is not listed in the associated 'flashfile.xml' will be totally ignored during +the fastboot upgrade procedure. + +Update method: qmi-pdc +---------------------- + +If the device supports the 'qmi-pdc' update method, the contents of the CAB +file should include files named as 'mcfg.*.mbn' which will be treated as MCFG +configuration files to download into the device using the Persistent Device +Configuration QMI service. + +If a device supports both 'fastboot' and 'qmi-pdc' methods, the fastboot +operation will always be run before the QMI operation, so that e.g. the full +partition where the MCFG files are stored can be wiped out before installing +the new ones. + diff -Nru fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-common.c fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-common.c --- fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-common.h fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-common.h --- fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_NITROKEY_COMMON_H -#define __FU_NITROKEY_COMMON_H +#pragma once #include @@ -15,6 +13,54 @@ guint32 fu_nitrokey_perform_crc32 (const guint8 *data, gsize size); -G_END_DECLS +#define NITROKEY_TRANSACTION_TIMEOUT 100 /* ms */ +#define NITROKEY_NR_RETRIES 5 + +#define NITROKEY_REQUEST_DATA_LENGTH 59 +#define NITROKEY_REPLY_DATA_LENGTH 53 -#endif /* __FU_NITROKEY_COMMON_H */ +#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 diff -Nru fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-device.c fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-device.c --- fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -8,63 +7,12 @@ #include "config.h" #include -#include #include "fu-nitrokey-common.h" #include "fu-nitrokey-device.h" 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) { @@ -208,55 +156,30 @@ } static gboolean -fu_nitrokey_device_probe (FuUsbDevice *device, GError **error) +fu_nitrokey_device_open (FuUsbDevice *device, GError **error) { GUsbDevice *usb_device = fu_usb_device_get_dev (device); - /* not the right kind of device */ - if (g_usb_device_get_vid (usb_device) != 0x20a0 || - g_usb_device_get_pid (usb_device) != 0x4109) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported with this device"); + /* claim interface */ + if (!g_usb_device_claim_interface (usb_device, 0x02, /* idx */ + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to do claim nitrokey: "); return FALSE; } - /* harcoded */ - fu_device_set_name (FU_DEVICE (device), "Nitrokey Storage"); - fu_device_set_vendor (FU_DEVICE (device), "Nitrokey"); - fu_device_set_summary (FU_DEVICE (device), "A secure memory stick"); - fu_device_add_icon (FU_DEVICE (device), "media-removable"); - - /* also add the USB VID:PID hash of the bootloader */ - fu_device_add_guid (FU_DEVICE (device), "USB\\VID_03EB&PID_2FF1"); - fu_device_set_remove_delay (FU_DEVICE (device), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); - - /* allowed, but requires manual bootloader step */ - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION); - /* success */ return TRUE; } static gboolean -fu_nitrokey_device_open (FuUsbDevice *device, GError **error) +fu_nitrokey_device_setup (FuDevice *device, GError **error) { - GUsbDevice *usb_device = fu_usb_device_get_dev (device); + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); NitrokeyGetDeviceStatusPayload payload; guint8 buf_reply[NITROKEY_REPLY_DATA_LENGTH]; g_autofree gchar *version = NULL; - /* claim interface */ - if (!g_usb_device_claim_interface (usb_device, 0x02, /* idx */ - G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, - error)) { - g_prefix_error (error, "failed to do claim nitrokey: "); - return FALSE; - } - /* get firmware version */ if (!nitrokey_execute_cmd_full (usb_device, NITROKEY_CMD_GET_DEVICE_STATUS, @@ -267,9 +190,9 @@ 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); - fu_device_set_version (FU_DEVICE (device), version); + memcpy (&payload, buf_reply, sizeof(payload)); + version = g_strdup_printf ("%u.%u", payload.VersionMajor, payload.VersionMinor); + fu_device_set_version (FU_DEVICE (device), version, FWUPD_VERSION_FORMAT_PAIR); /* success */ return TRUE; @@ -293,23 +216,24 @@ static void fu_nitrokey_device_init (FuNitrokeyDevice *device) { + fu_device_set_remove_delay (FU_DEVICE (device), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); } static void fu_nitrokey_device_class_init (FuNitrokeyDeviceClass *klass) { + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->setup = fu_nitrokey_device_setup; klass_usb_device->open = fu_nitrokey_device_open; klass_usb_device->close = fu_nitrokey_device_close; - klass_usb_device->probe = fu_nitrokey_device_probe; } FuNitrokeyDevice * -fu_nitrokey_device_new (GUsbDevice *usb_device) +fu_nitrokey_device_new (FuUsbDevice *device) { - FuNitrokeyDevice *device; - device = g_object_new (FU_TYPE_NITROKEY_DEVICE, - "usb-device", usb_device, - NULL); - return FU_NITROKEY_DEVICE (device); + FuNitrokeyDevice *self = g_object_new (FU_TYPE_NITROKEY_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return FU_NITROKEY_DEVICE (self); } diff -Nru fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-device.h fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-device.h --- fwupd-1.0.9/plugins/nitrokey/fu-nitrokey-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/fu-nitrokey-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_NITROKEY_DEVICE_H -#define __FU_NITROKEY_DEVICE_H - -#include -#include +#pragma once #include "fu-plugin.h" @@ -23,8 +18,6 @@ FuUsbDeviceClass parent_class; }; -FuNitrokeyDevice *fu_nitrokey_device_new (GUsbDevice *usb_device); +FuNitrokeyDevice *fu_nitrokey_device_new (FuUsbDevice *device); G_END_DECLS - -#endif /* __FU_NITROKEY_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/nitrokey/fu-plugin-nitrokey.c fwupd-1.2.10/plugins/nitrokey/fu-plugin-nitrokey.c --- fwupd-1.0.9/plugins/nitrokey/fu-plugin-nitrokey.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/fu-plugin-nitrokey.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,25 +6,31 @@ #include "config.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" #include "fu-nitrokey-device.h" #include "fu-nitrokey-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_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); +} + gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) { g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuNitrokeyDevice) device = NULL; + g_autoptr(FuNitrokeyDevice) dev = NULL; /* open the device */ - device = fu_nitrokey_device_new (usb_device); - locker = fu_device_locker_new (device, error); + dev = fu_nitrokey_device_new (device); + locker = fu_device_locker_new (dev, error); if (locker == NULL) return FALSE; /* success */ - fu_plugin_device_add (plugin, FU_DEVICE (device)); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } diff -Nru fwupd-1.0.9/plugins/nitrokey/fu-self-test.c fwupd-1.2.10/plugins/nitrokey/fu-self-test.c --- fwupd-1.0.9/plugins/nitrokey/fu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -12,6 +11,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) { const guint8 buf[] = { 0x00, 0x01, 0x02, 0x03, @@ -32,5 +84,7 @@ /* 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 -Nru fwupd-1.0.9/plugins/nitrokey/meson.build fwupd-1.2.10/plugins/nitrokey/meson.build --- fwupd-1.0.9/plugins/nitrokey/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,11 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginNitrokey"'] +install_data(['nitrokey.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_nitrokey', + fu_hash, sources : [ 'fu-nitrokey-device.c', 'fu-nitrokey-common.c', @@ -13,6 +18,9 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, @@ -22,6 +30,7 @@ if get_option('tests') e = executable( 'nitrokey-self-test', + fu_hash, sources : [ 'fu-nitrokey-common.c', 'fu-self-test.c', diff -Nru fwupd-1.0.9/plugins/nitrokey/nitrokey.quirk fwupd-1.2.10/plugins/nitrokey/nitrokey.quirk --- fwupd-1.0.9/plugins/nitrokey/nitrokey.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/nitrokey.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,7 @@ +# Nitrokey Storage +[DeviceInstanceId=USB\VID_20A0&PID_4109] +Plugin = nitrokey +Flags = needs-bootloader,use-runtime-version +CounterpartGuid = USB\VID_03EB&PID_2FF1 +Summary = A secure memory stick +Icon = media-removable diff -Nru fwupd-1.0.9/plugins/nitrokey/README.md fwupd-1.2.10/plugins/nitrokey/README.md --- fwupd-1.0.9/plugins/nitrokey/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/nitrokey/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -10,3 +10,12 @@ 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 -Nru fwupd-1.0.9/plugins/nvme/fu-nvme-common.c fwupd-1.2.10/plugins/nvme/fu-nvme-common.c --- fwupd-1.0.9/plugins/nvme/fu-nvme-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/fu-nvme-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/nvme/fu-nvme-common.h fwupd-1.2.10/plugins/nvme/fu-nvme-common.h --- fwupd-1.0.9/plugins/nvme/fu-nvme-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/fu-nvme-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/nvme/fu-nvme-device.c fwupd-1.2.10/plugins/nvme/fu-nvme-device.c --- fwupd-1.0.9/plugins/nvme/fu-nvme-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/fu-nvme-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include + +#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; + guint pci_depth; + gint fd; + guint64 write_block_size; +}; + +G_DEFINE_TYPE (FuNvmeDevice, fu_nvme_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 + +static void +fu_nvme_device_to_string (FuDevice *device, GString *str) +{ + FuNvmeDevice *self = FU_NVME_DEVICE (device); + g_string_append (str, " FuNvmeDevice:\n"); + g_string_append_printf (str, " fd:\t\t\t%i\n", self->fd); + g_string_append_printf (str, " pci-depth:\t\t%u\n", self->pci_depth); +} + +/* @addr_start and @addr_end are *inclusive* to match the NMVe specification */ +static gchar * +fu_nvme_device_get_string_safe (const guint8 *buf, guint16 addr_start, guint16 addr_end) +{ + GString *str; + + g_return_val_if_fail (buf != NULL, NULL); + g_return_val_if_fail (addr_start < addr_end, NULL); + + str = g_string_new_len (NULL, addr_end + addr_start + 1); + for (guint16 i = addr_start; i <= addr_end; i++) { + gchar tmp = (gchar) buf[i]; + /* skip leading spaces */ + if (g_ascii_isspace (tmp) && str->len == 0) + continue; + if (g_ascii_isprint (tmp)) + g_string_append_c (str, tmp); + } + + /* nothing found */ + if (str->len == 0) { + g_string_free (str, TRUE); + return NULL; + } + return g_strchomp (g_string_free (str, FALSE)); +} + +static gchar * +fu_nvme_device_get_guid_safe (const guint8 *buf, guint16 addr_start) +{ + if (!fu_common_guid_is_plausible (buf + addr_start)) + return NULL; + return fwupd_guid_to_string ((const fwupd_guid_t *) (buf + addr_start), + FWUPD_GUID_FLAG_MIXED_ENDIAN); +} + +static gboolean +fu_nvme_device_submit_admin_passthru (FuNvmeDevice *self, struct nvme_admin_cmd *cmd, GError **error) +{ + 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, + "failed to issue admin command 0x%02x: %s", + cmd->opcode, + strerror (errno)); + return FALSE; + } + + /* 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 +fu_nvme_device_identify_ctrl (FuNvmeDevice *self, guint8 *data, GError **error) +{ + struct nvme_admin_cmd cmd = { + .opcode = 0x06, + .nsid = 0x00, + .addr = 0x0, /* memory address of data */ + .data_len = FU_NVME_ID_CTRL_SIZE, + .cdw10 = 0x01, + .cdw11 = 0x00, + }; + memcpy (&cmd.addr, &data, sizeof (gpointer)); + return fu_nvme_device_submit_admin_passthru (self, &cmd, error); +} + +static gboolean +fu_nvme_device_fw_commit (FuNvmeDevice *self, + guint8 slot, + guint8 action, + guint8 bpid, + GError **error) +{ + struct nvme_admin_cmd cmd = { + .opcode = 0x10, + .cdw10 = (bpid << 31) | (action << 3) | slot, + }; + return fu_nvme_device_submit_admin_passthru (self, &cmd, error); +} + +static gboolean +fu_nvme_device_fw_download (FuNvmeDevice *self, + guint32 addr, + const guint8 *data, + guint32 data_sz, + GError **error) +{ + struct nvme_admin_cmd cmd = { + .opcode = 0x11, + .addr = 0x0, /* memory address of data */ + .data_len = data_sz, + .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); +} + +static void +fu_nvme_device_parse_cns_maybe_dell (FuNvmeDevice *self, const guint8 *buf) +{ + g_autofree gchar *component_id = NULL; + g_autofree gchar *devid = NULL; + g_autofree gchar *guid_efi = NULL; + g_autofree gchar *guid = NULL; + + /* add extra component ID if set */ + component_id = fu_nvme_device_get_string_safe (buf, 0xc36, 0xc3d); + if (component_id == NULL || + !g_str_is_ascii (component_id) || + strlen (component_id) < 6) { + g_debug ("invalid component ID, skipping"); + return; + } + + /* do not add the FuUdevDevice instance IDs as generic firmware + * should not be used on these OEM-specific devices */ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS); + + /* add instance ID *and* GUID as using no-auto-instance-ids */ + devid = g_strdup_printf ("STORAGE-DELL-%s", component_id); + fu_device_add_instance_id (FU_DEVICE (self), devid); + guid = fwupd_guid_hash_string (devid); + fu_device_add_guid (FU_DEVICE (self), guid); + + /* also add the EFI GUID */ + guid_efi = fu_nvme_device_get_guid_safe (buf, 0x0c26); + if (guid_efi != NULL) + fu_device_add_guid (FU_DEVICE (self), guid_efi); +} + +static gboolean +fu_nvme_device_set_version (FuNvmeDevice *self, const gchar *version, GError **error) +{ + FwupdVersionFormat fmt = fu_device_get_version_format (FU_DEVICE (self)); + + /* unset */ + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN || fmt == FWUPD_VERSION_FORMAT_PLAIN) { + fu_device_set_version (FU_DEVICE (self), version, FWUPD_VERSION_FORMAT_PLAIN); + return TRUE; + } + + /* AA.BB.CC.DD */ + if (fmt == FWUPD_VERSION_FORMAT_QUAD) { + guint64 tmp = g_ascii_strtoull (version, NULL, 16); + g_autofree gchar *version_new = NULL; + if (tmp == 0 || tmp > G_MAXUINT32) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%s is not valid 32 bit number", + version); + return FALSE; + } + version_new = fu_common_version_from_uint32 (tmp, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version (FU_DEVICE (self), version_new, fmt); + return TRUE; + } + + /* invalid, or not supported */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "version format %s not handled", + fwupd_version_format_to_string (fmt)); + return FALSE; +} + +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; + + /* wrong size */ + if (sz != FU_NVME_ID_CTRL_SIZE) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to parse blob, expected 0x%04x bytes", + (guint) FU_NVME_ID_CTRL_SIZE); + return FALSE; + } + + /* get sanitiezed string from CNS -- see the following doc for offsets: + * NVM-Express-1_3c-2018.05.24-Ratified.pdf */ + sn = fu_nvme_device_get_string_safe (buf, 4, 23); + if (sn != NULL) + fu_device_set_serial (FU_DEVICE (self), sn); + mn = fu_nvme_device_get_string_safe (buf, 24, 63); + if (mn != NULL) + fu_device_set_name (FU_DEVICE (self), mn); + sr = fu_nvme_device_get_string_safe (buf, 64, 71); + if (sr != NULL) { + if (!fu_nvme_device_set_version (self, sr, error)) + 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" */ + fu_nvme_device_parse_cns_maybe_dell (self, buf); + + /* fall back to the device description */ + if (fu_device_get_guids (FU_DEVICE (self))->len == 0) { + g_debug ("no vendor GUID, falling back to mn"); + fu_device_add_instance_id (FU_DEVICE (self), mn); + } + return TRUE; +} + +static void +fu_nvme_device_dump (const gchar *title, const guint8 *buf, gsize sz) +{ + if (g_getenv ("FWPUD_NVME_VERBOSE") == NULL) + return; + g_print ("%s (%" G_GSIZE_FORMAT "):", title, sz); + for (gsize i = 0; i < sz; i++) { + if (i % 64 == 0) + g_print ("\naddr 0x%04x: ", (guint) i); + g_print ("%02x", buf[i]); + } + g_print ("\n"); +} + +static gboolean +fu_nvme_device_open (FuDevice *device, GError **error) +{ + FuNvmeDevice *self = FU_NVME_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_nvme_device_probe (FuUdevDevice *device, GError **error) +{ + FuNvmeDevice *self = FU_NVME_DEVICE (device); + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id (device, "pci", 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); + + /* 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; +} + +static gboolean +fu_nvme_device_setup (FuDevice *device, GError **error) +{ + FuNvmeDevice *self = FU_NVME_DEVICE (device); + guint8 buf[FU_NVME_ID_CTRL_SIZE] = { 0x0 }; + + /* get and parse CNS */ + if (!fu_nvme_device_identify_ctrl (self, buf, error)) { + g_prefix_error (error, "failed to identify %s: ", + fu_device_get_physical_id (FU_DEVICE (self))); + return FALSE; + } + fu_nvme_device_dump ("CNS", buf, sizeof (buf)); + if (!fu_nvme_device_parse_cns (self, buf, sizeof(buf), error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_nvme_device_close (FuDevice *device, GError **error) +{ + FuNvmeDevice *self = FU_NVME_DEVICE (device); + if (!g_close (self->fd, error)) + return FALSE; + self->fd = 0; + return TRUE; +} + +static gboolean +fu_nvme_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + 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 (fw2, + 0x00, /* start_addr */ + 0x00, /* page_sz */ + block_size); /* block size */ + + /* write each block */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (!fu_nvme_device_fw_download (self, + 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); + } + + /* commit */ + if (!fu_nvme_device_fw_commit (self, + 0x00, /* let controller choose */ + 0x01, /* download replaces, activated on reboot */ + 0x00, /* boot partition identifier */ + error)) { + g_prefix_error (error, "failed to commit to auto slot: "); + return FALSE; + } + + /* success! */ + fu_device_set_progress (device, 100); + return TRUE; +} + +static gboolean +fu_nvme_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuNvmeDevice *self = FU_NVME_DEVICE (device); + if (g_strcmp0 (key, "NvmeBlockSize") == 0) { + self->write_block_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_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_set_summary (FU_DEVICE (self), "NVM Express Solid State Drive"); + fu_device_add_icon (FU_DEVICE (self), "drive-harddisk"); +} + +static void +fu_nvme_device_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_nvme_device_parent_class)->finalize (object); +} + +static void +fu_nvme_device_class_init (FuNvmeDeviceClass *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_nvme_device_finalize; + klass_device->to_string = fu_nvme_device_to_string; + klass_device->set_quirk_kv = fu_nvme_device_set_quirk_kv; + klass_device->open = fu_nvme_device_open; + klass_device->setup = fu_nvme_device_setup; + klass_device->close = fu_nvme_device_close; + klass_device->write_firmware = fu_nvme_device_write_firmware; + klass_udev_device->probe = fu_nvme_device_probe; +} + +FuNvmeDevice * +fu_nvme_device_new (FuUdevDevice *device) +{ + FuNvmeDevice *self = g_object_new (FU_TYPE_NVME_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} + +FuNvmeDevice * +fu_nvme_device_new_from_blob (const guint8 *buf, gsize sz, GError **error) +{ + g_autoptr(FuNvmeDevice) self = g_object_new (FU_TYPE_NVME_DEVICE, NULL); + if (!fu_nvme_device_parse_cns (self, buf, sz, error)) + return NULL; + return g_steal_pointer (&self); +} diff -Nru fwupd-1.0.9/plugins/nvme/fu-nvme-device.h fwupd-1.2.10/plugins/nvme/fu-nvme-device.h --- fwupd-1.0.9/plugins/nvme/fu-nvme-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/fu-nvme-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_NVME_DEVICE (fu_nvme_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuNvmeDevice, fu_nvme_device, FU, NVME_DEVICE, FuUdevDevice) + +FuNvmeDevice *fu_nvme_device_new (FuUdevDevice *device); +FuNvmeDevice *fu_nvme_device_new_from_blob (const guint8 *buf, + gsize sz, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/nvme/fu-plugin-nvme.c fwupd-1.2.10/plugins/nvme/fu-plugin-nvme.c --- fwupd-1.0.9/plugins/nvme/fu-plugin-nvme.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/fu-plugin-nvme.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-nvme-device.h" + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + g_autoptr(FuNvmeDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "nvme") != 0) + return TRUE; + + dev = fu_nvme_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, "nvme"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.nvmexpress"); +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, flags, error); +} diff -Nru fwupd-1.0.9/plugins/nvme/fu-self-test.c fwupd-1.2.10/plugins/nvme/fu-self-test.c --- fwupd-1.0.9/plugins/nvme/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-device-private.h" +#include "fu-nvme-device.h" +#include "fu-test.h" + +static void +fu_nvme_cns_func (void) +{ + gboolean ret; + gsize sz; + g_autofree gchar *data = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuNvmeDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + path = fu_test_get_filename (TESTDATADIR, "TOSHIBA_THNSN5512GPU7.bin"); + g_assert_nonnull (path); + ret = g_file_get_contents (path, &data, &sz, &error); + g_assert_no_error (error); + g_assert (ret); + dev = fu_nvme_device_new_from_blob ((guint8 *)data, sz, &error); + g_assert_no_error (error); + g_assert_nonnull (dev); + fu_device_convert_instance_ids (FU_DEVICE (dev)); + g_assert_cmpstr (fu_device_get_name (FU_DEVICE (dev)), ==, "THNSN5512GPU7 TOSHIBA"); + g_assert_cmpstr (fu_device_get_version (FU_DEVICE (dev)), ==, "410557LA"); + g_assert_cmpstr (fu_device_get_serial (FU_DEVICE (dev)), ==, "37RSDEADBEEF"); + g_assert_cmpstr (fu_device_get_guid_default (FU_DEVICE (dev)), ==, "e1409b09-50cf-5aef-8ad8-760b9022f88d"); +} + +static void +fu_nvme_cns_all_func (void) +{ + const gchar *fn; + g_autofree gchar *path = NULL; + g_autoptr(GDir) dir = NULL; + + /* may or may not exist */ + path = fu_test_get_filename (TESTDATADIR, "blobs"); + if (path == NULL) + return; + dir = g_dir_open (path, 0, NULL); + while ((fn = g_dir_read_name (dir)) != NULL) { + gsize sz; + g_autofree gchar *data = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(FuNvmeDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + filename = g_build_filename (path, fn, NULL); + g_print ("parsing %s... ", filename); + if (!g_file_get_contents (filename, &data, &sz, &error)) { + g_print ("failed to load %s: %s\n", filename, error->message); + continue; + } + dev = fu_nvme_device_new_from_blob ((guint8 *) data, sz, &error); + if (dev == NULL) { + g_print ("failed to load %s: %s\n", filename, error->message); + continue; + } + g_assert_cmpstr (fu_device_get_name (FU_DEVICE (dev)), !=, NULL); + g_assert_cmpstr (fu_device_get_version (FU_DEVICE (dev)), !=, NULL); + g_assert_cmpstr (fu_device_get_serial (FU_DEVICE (dev)), !=, NULL); + g_print ("done\n"); + } +} + +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/cns", fu_nvme_cns_func); + g_test_add_func ("/fwupd/cns{all}", fu_nvme_cns_all_func); + return g_test_run (); +} diff -Nru fwupd-1.0.9/plugins/nvme/meson.build fwupd-1.2.10/plugins/nvme/meson.build --- fwupd-1.0.9/plugins/nvme/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,61 @@ +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 : [ + 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( + 'nvme-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-nvme-common.c', + 'fu-nvme-device.c', + ], + include_directories : [ + include_directories('..'), + include_directories('../..'), + include_directories('../../libfwupd'), + include_directories('../../src'), + ], + dependencies : [ + plugin_deps, + ], + link_with : [ + libfwupdprivate, + ], + c_args : cargs + ) + test('nvme-self-test', e) +endif diff -Nru fwupd-1.0.9/plugins/nvme/nvme.quirk fwupd-1.2.10/plugins/nvme/nvme.quirk --- fwupd-1.0.9/plugins/nvme/nvme.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/nvme.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,3 @@ +# Phison +[DeviceInstanceId=NVME\VEN_1987] +Flags = force-align,needs-shutdown diff -Nru fwupd-1.0.9/plugins/nvme/README.md fwupd-1.2.10/plugins/nvme/README.md --- fwupd-1.0.9/plugins/nvme/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,51 @@ +NVMe +==== + +Introduction +------------ + +This plugin adds support for NVMe storage hardware. Devices are enumerated from +the Identify Controller data structure and can be updated with appropriate +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 -Nru fwupd-1.0.9/plugins/nvme/tests/.gitignore fwupd-1.2.10/plugins/nvme/tests/.gitignore --- fwupd-1.0.9/plugins/nvme/tests/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/nvme/tests/.gitignore 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +blobs Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/nvme/tests/TOSHIBA_THNSN5512GPU7.bin and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/nvme/tests/TOSHIBA_THNSN5512GPU7.bin differ diff -Nru fwupd-1.0.9/plugins/README.md fwupd-1.2.10/plugins/README.md --- fwupd-1.0.9/plugins/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -11,6 +11,9 @@ in this project, please file an issue and share the spec. Patches are also welcome. +We will not accept plugins that upgrade hardware using a proprietary Linux +executable, library, or DBus interface. + Plugin interaction ------------------ Some plugins may be able to influence the behavior of other plugins. diff -Nru fwupd-1.0.9/plugins/redfish/fu-plugin-redfish.c fwupd-1.2.10/plugins/redfish/fu-plugin-redfish.c --- fwupd-1.0.9/plugins/redfish/fu-plugin-redfish.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/fu-plugin-redfish.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-redfish-client.h" +#include "fu-redfish-common.h" + +struct FuPluginData { + FuRedfishClient *client; +}; + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + + return fu_redfish_client_update (data->client, device, blob_fw, error); +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + GPtrArray *devices; + + /* get the list of devices */ + if (!fu_redfish_client_coldplug (data->client, error)) + return FALSE; + devices = fu_redfish_client_get_devices (data->client); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + fu_plugin_device_add (plugin, device); + } + return TRUE; +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + GBytes *smbios_data = fu_plugin_get_smbios_data (plugin, REDFISH_SMBIOS_TABLE_TYPE); + g_autofree gchar *redfish_uri = NULL; + g_autofree gchar *ca_check = NULL; + + /* read the conf file */ + redfish_uri = fu_plugin_get_config_value (plugin, "Uri"); + if (redfish_uri != NULL) { + g_autofree gchar *username = NULL; + g_autofree gchar *password = NULL; + const gchar *ip_str = NULL; + g_auto(GStrv) split = NULL; + guint64 port; + + if (g_str_has_prefix (redfish_uri, "https://")) { + fu_redfish_client_set_https (data->client, TRUE); + ip_str = redfish_uri + strlen ("https://"); + } else if (g_str_has_prefix (redfish_uri, "http://")) { + fu_redfish_client_set_https (data->client, FALSE); + ip_str = redfish_uri + strlen ("http://"); + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "in valid scheme"); + return FALSE; + } + + split = g_strsplit (ip_str, ":", 2); + fu_redfish_client_set_hostname (data->client, split[0]); + port = g_ascii_strtoull (split[1], NULL, 10); + if (port == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no port specified"); + return FALSE; + } + fu_redfish_client_set_port (data->client, port); + + username = fu_plugin_get_config_value (plugin, "Username"); + password = fu_plugin_get_config_value (plugin, "Password"); + if (username != NULL && password != NULL) { + fu_redfish_client_set_username (data->client, username); + fu_redfish_client_set_password (data->client, password); + } + } else { + if (smbios_data == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no SMBIOS table"); + return FALSE; + } + } + + ca_check = fu_plugin_get_config_value (plugin, "CACheck"); + if (ca_check != NULL && g_ascii_strcasecmp (ca_check, "false") == 0) + fu_redfish_client_set_cacheck (data->client, FALSE); + else + fu_redfish_client_set_cacheck (data->client, TRUE); + + return fu_redfish_client_setup (data->client, smbios_data, error); +} + +void +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 +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_object_unref (data->client); +} diff -Nru fwupd-1.0.9/plugins/redfish/fu-redfish-client.c fwupd-1.2.10/plugins/redfish/fu-redfish-client.c --- fwupd-1.0.9/plugins/redfish/fu-redfish-client.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/fu-redfish-client.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,850 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include + +#include "fwupd-error.h" +#include "fwupd-enums.h" + +#include "fu-device.h" + +#include "fu-redfish-client.h" +#include "fu-redfish-common.h" + +struct _FuRedfishClient +{ + GObject parent_instance; + SoupSession *session; + gchar *hostname; + guint port; + gchar *username; + gchar *password; + gchar *update_uri_path; + gchar *push_uri_path; + gboolean auth_created; + gboolean use_https; + gboolean cacheck; + GPtrArray *devices; +}; + +G_DEFINE_TYPE (FuRedfishClient, fu_redfish_client, G_TYPE_OBJECT) + +static void +fu_redfish_client_set_auth (FuRedfishClient *self, SoupURI *uri, + SoupMessage *msg) +{ + if ((self->username != NULL && self->password != NULL) && + self->auth_created == FALSE) { + /* + * Some redfish implementations miss WWW-Authenticate + * header for a 401 response, and SoupAuthManager couldn't + * generate SoupAuth accordingly. Since DSP0266 makes + * Basic Authorization a requirement for redfish, it shall be + * safe to use Basic Auth for all redfish implementations. + */ + SoupAuthManager *manager = SOUP_AUTH_MANAGER (soup_session_get_feature (self->session, SOUP_TYPE_AUTH_MANAGER)); + g_autoptr(SoupAuth) auth = soup_auth_new (SOUP_TYPE_AUTH_BASIC, + msg, "Basic"); + soup_auth_authenticate (auth, self->username, self->password); + soup_auth_manager_use_auth (manager, uri, auth); + self->auth_created = TRUE; + } +} + +static GBytes * +fu_redfish_client_fetch_data (FuRedfishClient *self, const gchar *uri_path, GError **error) +{ + guint status_code; + g_autoptr(SoupMessage) msg = NULL; + g_autoptr(SoupURI) uri = NULL; + + /* create URI */ + uri = soup_uri_new (NULL); + soup_uri_set_scheme (uri, self->use_https ? "https" : "http"); + soup_uri_set_path (uri, uri_path); + soup_uri_set_host (uri, self->hostname); + soup_uri_set_port (uri, self->port); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + if (msg == NULL) { + g_autofree gchar *tmp = soup_uri_to_string (uri, FALSE); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to create message for URI %s", tmp); + return NULL; + } + fu_redfish_client_set_auth (self, uri, msg); + status_code = soup_session_send_message (self->session, msg); + if (status_code != SOUP_STATUS_OK) { + g_autofree gchar *tmp = soup_uri_to_string (uri, FALSE); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to download %s: %s", + tmp, soup_status_get_phrase (status_code)); + return NULL; + } + return g_bytes_new (msg->response_body->data, msg->response_body->length); +} + +static gboolean +fu_redfish_client_coldplug_member (FuRedfishClient *self, + JsonObject *member, + GError **error) +{ + g_autoptr(FuDevice) dev = NULL; + const gchar *guid = NULL; + g_autofree gchar *id = NULL; + + if (json_object_has_member (member, "SoftwareId")) { + guid = json_object_get_string_member (member, "SoftwareId"); + } else if (json_object_has_member (member, "Oem")) { + JsonObject *oem = json_object_get_object_member (member, "Oem"); + if (oem != NULL && json_object_has_member (oem, "Hpe")) { + JsonObject *hpe = json_object_get_object_member (oem, "Hpe"); + if (hpe != NULL && json_object_has_member (hpe, "DeviceClass")) + guid = json_object_get_string_member (hpe, "DeviceClass"); + } + } + + /* skip the devices without guid */ + if (guid == NULL) + return TRUE; + + dev = fu_device_new (); + + id = g_strdup_printf ("Redfish-Inventory-%s", + json_object_get_string_member (member, "Id")); + fu_device_set_id (dev, id); + + fu_device_add_guid (dev, guid); + if (json_object_has_member (member, "Name")) + fu_device_set_name (dev, json_object_get_string_member (member, "Name")); + fu_device_set_summary (dev, "Redfish device"); + if (json_object_has_member (member, "Version")) { + fu_device_set_version (dev, json_object_get_string_member (member, "Version"), + FWUPD_VERSION_FORMAT_UNKNOWN); + } + if (json_object_has_member (member, "LowestSupportedVersion")) + fu_device_set_version_lowest (dev, json_object_get_string_member (member, "LowestSupportedVersion")); + if (json_object_has_member (member, "Description")) + fu_device_set_description (dev, json_object_get_string_member (member, "Description")); + if (json_object_has_member (member, "Updateable")) { + if (json_object_get_boolean_member (member, "Updateable")) + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); + } else { + /* assume the device is updatable */ + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); + } + + /* success */ + g_ptr_array_add (self->devices, g_steal_pointer (&dev)); + return TRUE; +} + +static gboolean +fu_redfish_client_coldplug_collection (FuRedfishClient *self, + JsonObject *collection, + GError **error) +{ + JsonArray *members; + JsonNode *node_root; + JsonObject *member; + + members = json_object_get_array_member (collection, "Members"); + for (guint i = 0; i < json_array_get_length (members); i++) { + g_autoptr(JsonParser) parser = json_parser_new (); + g_autoptr(GBytes) blob = NULL; + JsonObject *member_id; + const gchar *member_uri; + + member_id = json_array_get_object_element (members, i); + member_uri = json_object_get_string_member (member_id, "@odata.id"); + if (member_uri == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no @odata.id string"); + return FALSE; + } + + /* try to connect */ + blob = fu_redfish_client_fetch_data (self, member_uri, error); + if (blob == NULL) + return FALSE; + + /* get the member object */ + if (!json_parser_load_from_data (parser, + g_bytes_get_data (blob, NULL), + (gssize) g_bytes_get_size (blob), + error)) { + g_prefix_error (error, "failed to parse node: "); + return FALSE; + } + node_root = json_parser_get_root (parser); + if (node_root == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no root node"); + return FALSE; + } + member = json_node_get_object (node_root); + if (member == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no member object"); + return FALSE; + } + + /* Create the device for the member */ + if (!fu_redfish_client_coldplug_member (self, member, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_redfish_client_coldplug_inventory (FuRedfishClient *self, + JsonObject *inventory, + GError **error) +{ + g_autoptr(JsonParser) parser = json_parser_new (); + g_autoptr(GBytes) blob = NULL; + JsonNode *node_root; + JsonObject *collection; + const gchar *collection_uri; + + if (inventory == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no inventory object"); + return FALSE; + } + + collection_uri = json_object_get_string_member (inventory, "@odata.id"); + if (collection_uri == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no @odata.id string"); + return FALSE; + } + + /* try to connect */ + blob = fu_redfish_client_fetch_data (self, collection_uri, error); + if (blob == NULL) + return FALSE; + + /* get the inventory object */ + if (!json_parser_load_from_data (parser, + g_bytes_get_data (blob, NULL), + (gssize) g_bytes_get_size (blob), + error)) { + g_prefix_error (error, "failed to parse node: "); + return FALSE; + } + node_root = json_parser_get_root (parser); + if (node_root == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no root node"); + return FALSE; + } + collection = json_node_get_object (node_root); + if (collection == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no collection object"); + return FALSE; + } + + return fu_redfish_client_coldplug_collection (self, collection, error); +} + +gboolean +fu_redfish_client_coldplug (FuRedfishClient *self, GError **error) +{ + JsonNode *node_root; + JsonObject *obj_root = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(JsonParser) parser = json_parser_new (); + + /* nothing set */ + if (self->update_uri_path == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no update_uri_path"); + return FALSE; + } + + /* try to connect */ + blob = fu_redfish_client_fetch_data (self, self->update_uri_path, error); + if (blob == NULL) + return FALSE; + + /* get the update service */ + if (!json_parser_load_from_data (parser, + g_bytes_get_data (blob, NULL), + (gssize) g_bytes_get_size (blob), + error)) { + g_prefix_error (error, "failed to parse node: "); + return FALSE; + } + node_root = json_parser_get_root (parser); + if (node_root == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no root node"); + return FALSE; + } + obj_root = json_node_get_object (node_root); + if (obj_root == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no root object"); + return FALSE; + } + if (!json_object_get_boolean_member (obj_root, "ServiceEnabled")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "service is not enabled"); + return FALSE; + } + if (!json_object_has_member (obj_root, "HttpPushUri")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HttpPushUri is not available"); + return FALSE; + } + self->push_uri_path = g_strdup (json_object_get_string_member (obj_root, "HttpPushUri")); + if (self->push_uri_path == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HttpPushUri is invalid"); + return FALSE; + } + if (json_object_has_member (obj_root, "FirmwareInventory")) { + JsonObject *tmp = json_object_get_object_member (obj_root, "FirmwareInventory"); + return fu_redfish_client_coldplug_inventory (self, tmp, error); + } + if (json_object_has_member (obj_root, "SoftwareInventory")) { + JsonObject *tmp = json_object_get_object_member (obj_root, "SoftwareInventory"); + return fu_redfish_client_coldplug_inventory (self, tmp, error); + } + return TRUE; +} + +static gboolean +fu_redfish_client_set_uefi_credentials (FuRedfishClient *self, GError **error) +{ + guint32 indications_le; + g_autofree gchar *userpass_safe = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GBytes) indications = NULL; + g_autoptr(GBytes) userpass = NULL; + + /* get the uint32 specifying if there are EFI variables set */ + indications = fu_redfish_common_get_evivar_raw (REDFISH_EFI_INFORMATION_GUID, + REDFISH_EFI_INFORMATION_INDICATIONS, + error); + if (indications == NULL) + return FALSE; + if (g_bytes_get_size (indications) != 4) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid value for %s, got %" G_GSIZE_FORMAT " bytes", + REDFISH_EFI_INFORMATION_INDICATIONS, + g_bytes_get_size (indications)); + return FALSE; + } + memcpy (&indications_le, g_bytes_get_data (indications, NULL), 4); + if ((indications_le & REDFISH_EFI_INDICATIONS_OS_CREDENTIALS) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no indications for OS credentials"); + return FALSE; + } + + /* read the correct EFI var for runtime */ + userpass = fu_redfish_common_get_evivar_raw (REDFISH_EFI_INFORMATION_GUID, + REDFISH_EFI_INFORMATION_OS_CREDENTIALS, + error); + if (userpass == NULL) + return FALSE; + + /* it might not be NUL terminated */ + userpass_safe = g_strndup (g_bytes_get_data (userpass, NULL), + g_bytes_get_size (userpass)); + split = g_strsplit (userpass_safe, ":", -1); + if (g_strv_length (split) != 2) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid format for username:password, got '%s'", + userpass_safe); + return FALSE; + } + fu_redfish_client_set_username (self, split[0]); + fu_redfish_client_set_password (self, split[1]); + return TRUE; +} + +static void +fu_redfish_client_parse_interface_data (const guint8 *buf, guint8 sz) +{ + switch (buf[0]) { + case REDFISH_INTERFACE_TYPE_USB_NEWORK: + g_debug ("USB Network Interface"); + /* + * uint16 idVendor(2-bytes) + * uint16 idProduct(2-bytes) + * uint8 SerialNumberLen: + * uint8 DescriptorType: + * uint8* SerialNumber: + */ + break; + case REDFISH_INTERFACE_TYPE_PCI_NEWORK: + g_debug ("PCI Network Interface"); + /* + * uint16 VendorID + * uint16 DeviceID + * uint16 Subsystem_Vendor_ID + * uint16 Subsystem_ID + */ + break; + default: + break; + } +} + +typedef struct __attribute__((packed)) { + guint8 service_uuid[16]; + guint8 host_ip_assignment_type; + guint8 host_ip_address_format; + guint8 host_ip_address[16]; + guint8 host_ip_mask[16]; + guint8 service_ip_assignment_type; + guint8 service_ip_address_format; + guint8 service_ip_address[16]; + guint8 service_ip_mask[16]; + guint16 service_ip_port; + guint32 service_ip_vlan_id; + guint8 service_hostname_len; + /* service_hostname; */ +} RedfishProtocolDataOverIp; + +static gboolean +fu_redfish_client_parse_protocol_data (FuRedfishClient *self, + const guint8 *buf, + guint8 sz, + GError **error) +{ + RedfishProtocolDataOverIp *pr; + if (sz < sizeof(RedfishProtocolDataOverIp)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "protocol data too small"); + return FALSE; + } + pr = (RedfishProtocolDataOverIp *) buf; + + /* parse the hostname and port */ + if (pr->service_ip_assignment_type == REDFISH_IP_ASSIGNMENT_TYPE_STATIC || + pr->service_ip_assignment_type == REDFISH_IP_ASSIGNMENT_TYPE_AUTO_CONFIG) { + if (pr->service_ip_address_format == REDFISH_IP_ADDRESS_FORMAT_V4) { + g_autofree gchar *tmp = NULL; + tmp = fu_redfish_common_buffer_to_ipv4 (pr->service_ip_address); + fu_redfish_client_set_hostname (self, tmp); + } else if (pr->service_ip_address_format == REDFISH_IP_ADDRESS_FORMAT_V6) { + g_autofree gchar *tmp = NULL; + tmp = fu_redfish_common_buffer_to_ipv6 (pr->service_ip_address); + fu_redfish_client_set_hostname (self, tmp); + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "address format is invalid"); + return FALSE; + } + fu_redfish_client_set_port (self, pr->service_ip_port); + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "DHCP address formats not supported (%0x2)", + pr->service_ip_assignment_type); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_redfish_client_set_smbios_interfaces (FuRedfishClient *self, + GBytes *smbios_table, + GError **error) +{ + const guint8 *buf; + gsize sz = 0; + + /* check size */ + buf = g_bytes_get_data (smbios_table, &sz); + if (sz < 0x09) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "SMBIOS entry too small: %" G_GSIZE_FORMAT, + sz); + return FALSE; + } + + /* check interface type */ + if (buf[0x04] != REDFISH_CONTROLLER_INTERFACE_TYPE_NETWORK_HOST) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "only Network Host Interface supported"); + return FALSE; + } + + /* check length */ + if (buf[0x05] > sz - 0x08) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "interface specific data too large %u > %" G_GSIZE_FORMAT, + buf[0x05], sz - 0x08); + return FALSE; + } + + /* parse data, for not just for debugging */ + if (buf[0x05] > 0) + fu_redfish_client_parse_interface_data (&buf[0x06], buf[0x05]); + + /* parse protocol records */ + for (guint8 i = 0x07 + buf[0x05]; i < sz - 1; i++) { + guint8 protocol_id = buf[i]; + guint8 protocol_sz = buf[i+1]; + if (protocol_sz > sz - buf[0x05] + 0x07) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "protocol length too large"); + return FALSE; + } + if (protocol_id == REDFISH_PROTOCOL_REDFISH_OVER_IP) { + if (!fu_redfish_client_parse_protocol_data (self, + &buf[i+2], + protocol_sz, + error)) + return FALSE; + } else { + g_debug ("ignoring unsupported protocol ID %02x", + protocol_id); + } + i += protocol_sz - 1; + } + + return TRUE; +} + +gboolean +fu_redfish_client_update (FuRedfishClient *self, FuDevice *device, GBytes *blob_fw, + GError **error) +{ + FwupdRelease *release; + g_autofree gchar *filename = NULL; + + guint status_code; + g_autoptr(SoupMessage) msg = NULL; + g_autoptr(SoupURI) uri = NULL; + g_autoptr(SoupMultipart) multipart = NULL; + g_autoptr(SoupBuffer) buffer = NULL; + g_autofree gchar *uri_str = NULL; + + /* Get the update version */ + release = fwupd_device_get_release_default (FWUPD_DEVICE (device)); + if (release != NULL) { + filename = g_strdup_printf ("%s-%s.bin", + fu_device_get_name (device), + fwupd_release_get_version (release)); + } else { + filename = g_strdup_printf ("%s.bin", + fu_device_get_name (device)); + } + + /* create URI */ + uri = soup_uri_new (NULL); + soup_uri_set_scheme (uri, self->use_https ? "https" : "http"); + soup_uri_set_path (uri, self->push_uri_path); + soup_uri_set_host (uri, self->hostname); + soup_uri_set_port (uri, self->port); + uri_str = soup_uri_to_string (uri, FALSE); + + /* Create the multipart request */ + multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART); + buffer = soup_buffer_new (SOUP_MEMORY_COPY, + g_bytes_get_data (blob_fw, NULL), + g_bytes_get_size (blob_fw)); + soup_multipart_append_form_file (multipart, filename, filename, + "application/octet-stream", + buffer); + msg = soup_form_request_new_from_multipart (uri_str, multipart); + if (msg == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to create message for URI %s", uri_str); + return FALSE; + } + fu_redfish_client_set_auth (self, uri, msg); + status_code = soup_session_send_message (self->session, msg); + if (status_code != SOUP_STATUS_OK) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to upload %s to %s: %s", + filename, uri_str, + soup_status_get_phrase (status_code)); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_redfish_client_setup (FuRedfishClient *self, GBytes *smbios_table, GError **error) +{ + JsonNode *node_root; + JsonObject *obj_root = NULL; + JsonObject *obj_update_service = NULL; + const gchar *data_id; + const gchar *version = NULL; + g_autofree gchar *user_agent = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(JsonParser) parser = json_parser_new (); + + /* sanity check */ + if (self->port == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no port specified"); + return FALSE; + } + if (self->port > 0xffff) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid port specified: 0x%x", + self->port); + return FALSE; + } + + /* create the soup session */ + user_agent = g_strdup_printf ("%s/%s", PACKAGE_NAME, PACKAGE_VERSION); + self->session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent, + SOUP_SESSION_TIMEOUT, 60, + NULL); + if (self->session == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to setup networking"); + return FALSE; + } + + if (self->cacheck == FALSE) { + g_object_set (G_OBJECT (self->session), + SOUP_SESSION_SSL_STRICT, FALSE, + NULL); + } + + /* this is optional */ + if (smbios_table != NULL) { + g_autoptr(GError) error_smbios = NULL; + g_autoptr(GError) error_uefi = NULL; + if (!fu_redfish_client_set_smbios_interfaces (self, + smbios_table, + &error_smbios)) { + g_debug ("failed to get connection URI automatically: %s", + error_smbios->message); + } + if (!fu_redfish_client_set_uefi_credentials (self, &error_uefi)) { + g_debug ("failed to get username and password automatically: %s", + error_uefi->message); + } + } + if (self->hostname != NULL) + g_debug ("Hostname: %s", self->hostname); + if (self->port != 0) + g_debug ("Port: %u", self->port); + if (self->username != NULL) + g_debug ("Username: %s", self->username); + if (self->password != NULL) + g_debug ("Password: %s", self->password); + + /* try to connect */ + blob = fu_redfish_client_fetch_data (self, "/redfish/v1/", error); + if (blob == NULL) + return FALSE; + + /* get the update service */ + if (!json_parser_load_from_data (parser, + g_bytes_get_data (blob, NULL), + (gssize) g_bytes_get_size (blob), + error)) { + g_prefix_error (error, "failed to parse node: "); + return FALSE; + } + node_root = json_parser_get_root (parser); + if (node_root == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no root node"); + return FALSE; + } + obj_root = json_node_get_object (node_root); + if (obj_root == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no root object"); + return FALSE; + } + if (json_object_has_member (obj_root, "ServiceVersion")) { + version = json_object_get_string_member (obj_root, + "ServiceVersion"); + } else if (json_object_has_member (obj_root, "RedfishVersion")) { + version = json_object_get_string_member (obj_root, + "RedfishVersion"); + } + g_debug ("Version: %s", version); + g_debug ("UUID: %s", + json_object_get_string_member (obj_root, "UUID")); + + if (json_object_has_member (obj_root, "UpdateService")) + obj_update_service = json_object_get_object_member (obj_root, "UpdateService"); + if (obj_update_service == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no UpdateService object"); + return FALSE; + } + data_id = json_object_get_string_member (obj_update_service, "@odata.id"); + if (data_id == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no @odata.id string"); + return FALSE; + } + self->update_uri_path = g_strdup (data_id); + return TRUE; +} + +GPtrArray * +fu_redfish_client_get_devices (FuRedfishClient *self) +{ + return self->devices; +} + +void +fu_redfish_client_set_hostname (FuRedfishClient *self, const gchar *hostname) +{ + g_free (self->hostname); + self->hostname = g_strdup (hostname); +} + +void +fu_redfish_client_set_port (FuRedfishClient *self, guint port) +{ + self->port = port; +} + +void +fu_redfish_client_set_https (FuRedfishClient *self, gboolean use_https) +{ + self->use_https = use_https; +} + +void +fu_redfish_client_set_cacheck (FuRedfishClient *self, gboolean cacheck) +{ + self->cacheck = cacheck; +} + +void +fu_redfish_client_set_username (FuRedfishClient *self, const gchar *username) +{ + g_free (self->username); + self->username = g_strdup (username); +} + +void +fu_redfish_client_set_password (FuRedfishClient *self, const gchar *password) +{ + g_free (self->password); + self->password = g_strdup (password); +} + +static void +fu_redfish_client_finalize (GObject *object) +{ + FuRedfishClient *self = FU_REDFISH_CLIENT (object); + if (self->session != NULL) + g_object_unref (self->session); + g_free (self->update_uri_path); + g_free (self->push_uri_path); + g_free (self->hostname); + g_free (self->username); + g_free (self->password); + g_ptr_array_unref (self->devices); + G_OBJECT_CLASS (fu_redfish_client_parent_class)->finalize (object); +} + +static void +fu_redfish_client_class_init (FuRedfishClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_redfish_client_finalize; +} + +static void +fu_redfish_client_init (FuRedfishClient *self) +{ + self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); +} + +FuRedfishClient * +fu_redfish_client_new (void) +{ + FuRedfishClient *self; + self = g_object_new (REDFISH_TYPE_CLIENT, NULL); + return FU_REDFISH_CLIENT (self); +} + +/* vim: set noexpandtab: */ diff -Nru fwupd-1.0.9/plugins/redfish/fu-redfish-client.h fwupd-1.2.10/plugins/redfish/fu-redfish-client.h --- fwupd-1.0.9/plugins/redfish/fu-redfish-client.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/fu-redfish-client.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,41 @@ + /* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define REDFISH_TYPE_CLIENT (fu_redfish_client_get_type ()) + +G_DECLARE_FINAL_TYPE (FuRedfishClient, fu_redfish_client, FU, REDFISH_CLIENT, GObject) + +FuRedfishClient *fu_redfish_client_new (void); +void fu_redfish_client_set_hostname (FuRedfishClient *self, + const gchar *hostname); +void fu_redfish_client_set_username (FuRedfishClient *self, + const gchar *username); +void fu_redfish_client_set_password (FuRedfishClient *self, + const gchar *password); +void fu_redfish_client_set_port (FuRedfishClient *self, + guint port); +void fu_redfish_client_set_https (FuRedfishClient *self, + gboolean use_https); +void fu_redfish_client_set_cacheck (FuRedfishClient *self, + gboolean cacheck); +gboolean fu_redfish_client_update (FuRedfishClient *self, + FuDevice *device, + GBytes *blob_fw, + GError **error); +gboolean fu_redfish_client_setup (FuRedfishClient *self, + GBytes *smbios_table, + GError **error); +gboolean fu_redfish_client_coldplug (FuRedfishClient *self, + GError **error); +GPtrArray *fu_redfish_client_get_devices (FuRedfishClient *self); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/redfish/fu-redfish-common.c fwupd-1.2.10/plugins/redfish/fu-redfish-common.c --- fwupd-1.0.9/plugins/redfish/fu-redfish-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/fu-redfish-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-error.h" + +#include "fu-redfish-common.h" + +GBytes * +fu_redfish_common_get_evivar_raw (efi_guid_t guid, const gchar *name, GError **error) +{ + gsize sz = 0; + guint32 attribs = 0; + guint8 *data = NULL; + + if (efi_get_variable (guid, name, &data, &sz, &attribs) < 0) { + g_autofree gchar *guid_str = NULL; + efi_guid_to_str (&guid, &guid_str); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to get efivar for %s %s", + guid_str, name); + return NULL; + } + return g_bytes_new_take (data, sz); +} + +gchar * +fu_redfish_common_buffer_to_ipv4 (const guint8 *buffer) +{ + GString *str = g_string_new (NULL); + for (guint i = 0; i < 4; i++) { + g_string_append_printf (str, "%u", buffer[i]); + if (i != 3) + g_string_append (str, "."); + } + return g_string_free (str, FALSE); +} + +gchar * +fu_redfish_common_buffer_to_ipv6 (const guint8 *buffer) +{ + GString *str = g_string_new (NULL); + for (guint i = 0; i < 16; i += 4) { + g_string_append_printf (str, "%02x%02x%02x%02x", + buffer[i+0], buffer[i+1], + buffer[i+2], buffer[i+3]); + if (i != 12) + g_string_append (str, ":"); + } + return g_string_free (str, FALSE); +} + +/* vim: set noexpandtab: */ diff -Nru fwupd-1.0.9/plugins/redfish/fu-redfish-common.h fwupd-1.2.10/plugins/redfish/fu-redfish-common.h --- fwupd-1.0.9/plugins/redfish/fu-redfish-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/fu-redfish-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,50 @@ + /* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +/* SMBIOS */ +#define REDFISH_SMBIOS_TABLE_TYPE 0x42 + +#define REDFISH_PROTOCOL_REDFISH_OVER_IP 0x04 + +#define REDFISH_CONTROLLER_INTERFACE_TYPE_NETWORK_HOST 0x40 + +#define REDFISH_INTERFACE_TYPE_USB_NEWORK 0x02 +#define REDFISH_INTERFACE_TYPE_PCI_NEWORK 0x03 + +#define REDFISH_IP_ASSIGNMENT_TYPE_STATIC 0x00 +#define REDFISH_IP_ASSIGNMENT_TYPE_DHCP 0x02 +#define REDFISH_IP_ASSIGNMENT_TYPE_AUTO_CONFIG 0x03 +#define REDFISH_IP_ASSIGNMENT_TYPE_HOST_SELECT 0x04 + +#define REDFISH_IP_ADDRESS_FORMAT_UNKNOWN 0x00 +#define REDFISH_IP_ADDRESS_FORMAT_V4 0x01 +#define REDFISH_IP_ADDRESS_FORMAT_V6 0x02 + +/* EFI */ +#define REDFISH_EFI_INFORMATION_GUID EFI_GUID(0x16faa37e,0x4b6a,0x4891,0x9028,0x24,0x2d,0xe6,0x5a,0x3b,0x70) + +#define REDFISH_EFI_INFORMATION_INDICATIONS "RedfishIndications" +#define REDFISH_EFI_INFORMATION_FW_CREDENTIALS "RedfishFWCredentials" +#define REDFISH_EFI_INFORMATION_OS_CREDENTIALS "RedfishOSCredentials" + +#define REDFISH_EFI_INDICATIONS_FW_CREDENTIALS 0x00000001 +#define REDFISH_EFI_INDICATIONS_OS_CREDENTIALS 0x00000002 + +/* shared */ +GBytes *fu_redfish_common_get_evivar_raw (efi_guid_t guid, + const gchar *name, + GError **error); +gchar *fu_redfish_common_buffer_to_ipv4 (const guint8 *buffer); +gchar *fu_redfish_common_buffer_to_ipv6 (const guint8 *buffer); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/redfish/fu-self-test.c fwupd-1.2.10/plugins/redfish/fu-self-test.c --- fwupd-1.0.9/plugins/redfish/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-plugin-private.h" +#include "fu-test.h" + +#include "fu-redfish-common.h" + +static void +fu_test_redfish_common_func (void) +{ + const guint8 buf[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + g_autofree gchar *ipv4 = NULL; + g_autofree gchar *ipv6 = NULL; + + ipv4 = fu_redfish_common_buffer_to_ipv4 (buf); + g_assert_cmpstr (ipv4, ==, "0.1.2.3"); + ipv6 = fu_redfish_common_buffer_to_ipv6 (buf); + g_assert_cmpstr (ipv6, ==, "00010203:04050607:08090a0b:0c0d0e0f"); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + g_test_add_func ("/redfish/common", fu_test_redfish_common_func); + return g_test_run (); +} diff -Nru fwupd-1.0.9/plugins/redfish/meson.build fwupd-1.2.10/plugins/redfish/meson.build --- fwupd-1.0.9/plugins/redfish/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,57 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginRedfish"'] + +shared_module('fu_plugin_redfish', + fu_hash, + sources : [ + 'fu-plugin-redfish.c', + 'fu-redfish-client.c', + 'fu-redfish-common.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, + efivar, + libjsonglib, + ], +) + +install_data(['redfish.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') +) + +if get_option('tests') + e = executable( + 'redfish-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-redfish-client.c', + 'fu-redfish-common.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + dependencies : [ + plugin_deps, + efivar, + libjsonglib, + ], + link_with : [ + libfwupdprivate, + ], + c_args : cargs + ) + test('redfish-self-test', e) +endif diff -Nru fwupd-1.0.9/plugins/redfish/README.md fwupd-1.2.10/plugins/redfish/README.md --- fwupd-1.0.9/plugins/redfish/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,70 @@ +Redfish Support +=============== + +Introduction +------------ + +Redfish is an open industry standard specification and schema that helps enable +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 +--------------------------- + +The service IP may not be automatically discoverable due to the absence of +Type 0x42 entry in SMBIOS. In this case, you have to specify the service IP +to RedfishUri in /etc/fwupd/redfish.conf + +Take HPE Gen10 for example, the service IP can be found with the following +command: + + # ilorest --nologo list --selector=EthernetInterface. -j + +This command lists all network interfaces, and the Redfish service IP belongs +to one of "Manager Network" Interfaces. For example: + + { + "@odata.context": "/redfish/v1/$metadata#EthernetInterface.EthernetInterface", + "@odata.id": "/redfish/v1/Managers/1/EthernetInterfaces/1/", + "@odata.type": "#EthernetInterface.v1_0_3.EthernetInterface", + "Description": "Configuration of this Manager Network Interface", + "HostName": "myredfish", + "IPv4Addresses": [ + { + "SubnetMask": "255.255.255.0", + "AddressOrigin": "DHCP", + "Gateway": "192.168.0.1", + "Address": "192.168.0.133" + } + ], + ... + +In this example, the service IP is "192.168.0.133". + +Since the conventional HTTP port is 80 and HTTPS port is 443, we can set +RedfishUri to either "http://192.168.0.133:80" or "https://192.168.0.133:443" +and verify the uri with + + $ curl http://192.168.0.133:80/redfish/v1/ + +or + + $ curl -k https://192.168.0.133:443/redfish/v1/ diff -Nru fwupd-1.0.9/plugins/redfish/redfish.conf fwupd-1.2.10/plugins/redfish/redfish.conf --- fwupd-1.0.9/plugins/redfish/redfish.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/redfish/redfish.conf 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,14 @@ +[redfish] + +# The URI to the Redfish service in the format ://: +# ex: https://192.168.0.133:443 +#Uri= + +# The username and password to the Redfish service +#Username= +#Password= + +# Whether to verify the server certificate or not +# Expected value: TRUE or FALSE +# Default: TRUE +#CACheck= diff -Nru fwupd-1.0.9/plugins/rts54hid/fu-plugin-rts54hid.c fwupd-1.2.10/plugins/rts54hid/fu-plugin-rts54hid.c --- fwupd-1.0.9/plugins/rts54hid/fu-plugin-rts54hid.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/fu-plugin-rts54hid.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-rts54hid-device.h" +#include "fu-rts54hid-module.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.realtek.rts54"); + + /* register the custom types */ + g_type_ensure (FU_TYPE_RTS54HID_MODULE); +} + +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, flags, error); +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuRts54HidDevice) dev = NULL; + + /* open the device */ + dev = fu_rts54hid_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + + /* success */ + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-common.h fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-common.h --- fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2018 Realtek Semiconductor Corporation + * Copyright (C) 2018 Dell Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#define FU_RTS54HID_TRANSFER_BLOCK_SIZE 0x80 +#define FU_RTS54HID_REPORT_LENGTH 0xc0 + +/* [vendor-cmd:64] [data-payload:128] */ +#define FU_RTS54HID_CMD_BUFFER_OFFSET_DATA 0x40 + +typedef struct __attribute__ ((packed)) { + guint8 slave_addr; + guint8 data_sz; + guint8 speed; +} FuRts54HidI2cParameters; + +typedef struct __attribute__ ((packed)) { + guint8 cmd; + guint8 ext; + union { + guint32 dwregaddr; + struct { + guint8 cmd_data0; + guint8 cmd_data1; + guint8 cmd_data2; + guint8 cmd_data3; + }; + }; + guint16 bufferlen; + union { + FuRts54HidI2cParameters parameters_i2c; + guint32 parameters; + }; +} FuRts54HidCmdBuffer; + +typedef enum { + FU_RTS54HID_I2C_SPEED_250K, + FU_RTS54HID_I2C_SPEED_400K, + FU_RTS54HID_I2C_SPEED_800K, + /* */ + FU_RTS54HID_I2C_SPEED_LAST, +} FuRts54HidI2cSpeed; + +typedef enum { + FU_RTS54HID_CMD_READ_DATA = 0xc0, + FU_RTS54HID_CMD_WRITE_DATA = 0x40, + /* */ + FU_RTS54HID_CMD_LAST, +} FuRts54HidCmd; + +typedef enum { + FU_RTS54HID_EXT_MCUMODIFYCLOCK = 0x06, + FU_RTS54HID_EXT_READ_STATUS = 0x09, + FU_RTS54HID_EXT_I2C_WRITE = 0xc6, + FU_RTS54HID_EXT_WRITEFLASH = 0xc8, + FU_RTS54HID_EXT_I2C_READ = 0xd6, + FU_RTS54HID_EXT_READFLASH = 0xd8, + FU_RTS54HID_EXT_VERIFYUPDATE = 0xd9, + FU_RTS54HID_EXT_ERASEBANK = 0xe8, + FU_RTS54HID_EXT_RESET2FLASH = 0xe9, + /* */ + FU_RTS54HID_EXT_LAST, +} FuRts54HidExt; diff -Nru fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-device.c fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-device.c --- fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-chunk.h" +#include "fu-rts54hid-common.h" +#include "fu-rts54hid-device.h" + +struct _FuRts54HidDevice { + FuUsbDevice parent_instance; + gboolean fw_auth; + gboolean dual_bank; +}; + +G_DEFINE_TYPE (FuRts54HidDevice, fu_rts54hid_device, FU_TYPE_USB_DEVICE) + +#define FU_RTS54HID_DEVICE_TIMEOUT 1000 /* ms */ + +static void +fu_rts54hid_device_to_string (FuDevice *device, GString *str) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device); + g_string_append (str, " FuRts54HidDevice:\n"); + g_string_append_printf (str, " fw-auth: %i\n", self->fw_auth); + g_string_append_printf (str, " dual-bank: %i\n", self->dual_bank); +} + +gboolean +fu_rts54hid_device_set_report (FuRts54HidDevice *self, + guint8 *buf, gsize buf_sz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_len = 0; + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + HID_REPORT_SET, + 0x0200, 0x0000, + buf, buf_sz, + &actual_len, + FU_RTS54HID_DEVICE_TIMEOUT * 2, + NULL, error)) { + g_prefix_error (error, "failed to SetReport: "); + return FALSE; + } + if (actual_len != buf_sz) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "only wrote %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + return TRUE; +} + +gboolean +fu_rts54hid_device_get_report (FuRts54HidDevice *self, + guint8 *buf, gsize buf_sz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_len = 0; + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + HID_REPORT_GET, + 0x0100, 0x0000, + buf, buf_sz, + &actual_len, /* actual length */ + FU_RTS54HID_DEVICE_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "failed to GetReport: "); + return FALSE; + } + if (actual_len != buf_sz) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "only read %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_set_clock_mode (FuRts54HidDevice *self, gboolean enable, GError **error) +{ + FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_MCUMODIFYCLOCK, + .cmd_data0 = (guint8) enable, + .cmd_data1 = 0, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = 0, + .parameters = 0, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + if (!fu_rts54hid_device_set_report (self, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to set clock-mode=%i: ", enable); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_reset_to_flash (FuRts54HidDevice *self, GError **error) +{ + FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_RESET2FLASH, + .dwregaddr = 0, + .bufferlen = 0, + .parameters = 0, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + if (!fu_rts54hid_device_set_report (self, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to soft reset: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_write_flash (FuRts54HidDevice *self, + guint32 addr, + const guint8 *data, + guint16 data_sz, + GError **error) +{ + FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_WRITEFLASH, + .dwregaddr = GUINT32_TO_LE (addr), + .bufferlen = GUINT16_TO_LE (data_sz), + .parameters = 0, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + + g_return_val_if_fail (data_sz <= 128, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data_sz != 0, FALSE); + + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + memcpy (buf + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, data, data_sz); + if (!fu_rts54hid_device_set_report (self, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to write flash @%08x: ", (guint) addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_verify_update_fw (FuRts54HidDevice *self, GError **error) +{ + const FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_VERIFYUPDATE, + .cmd_data0 = 1, + .cmd_data1 = 0, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = GUINT16_TO_LE (1), + .parameters = 0, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + + /* set then get */ + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + if (!fu_rts54hid_device_set_report (self, buf, sizeof(buf), error)) + return FALSE; + g_usleep (4 * G_USEC_PER_SEC); + if (!fu_rts54hid_device_get_report (self, buf, sizeof(buf), error)) + return FALSE; + + /* check device status */ + if (buf[0x40] != 0x01) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "firmware flash failed"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hid_device_erase_spare_bank (FuRts54HidDevice *self, GError **error) +{ + FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_ERASEBANK, + .cmd_data0 = 0, + .cmd_data1 = 1, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = 0, + .parameters = 0, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + if (!fu_rts54hid_device_set_report (self, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to erase spare bank: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_ensure_status (FuRts54HidDevice *self, GError **error) +{ + const FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_READ_DATA, + .ext = FU_RTS54HID_EXT_READ_STATUS, + .cmd_data0 = 0, + .cmd_data1 = 0, + .cmd_data2 = 0, + .cmd_data3 = 0, + .bufferlen = GUINT16_TO_LE (32), + .parameters = 0, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + g_autofree gchar *version = NULL; + + /* set then get */ + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + if (!fu_rts54hid_device_set_report (self, buf, sizeof(buf), error)) + return FALSE; + if (!fu_rts54hid_device_get_report (self, buf, sizeof(buf), error)) + return FALSE; + + /* check the hardware capabilities */ + self->dual_bank = (buf[0x40 + 7] & 0xf0) == 0x80; + self->fw_auth = (buf[0x40 + 13] & 0x02) > 0; + + /* hub version is more accurate than bcdVersion */ + version = g_strdup_printf ("%x.%x", buf[0x40 + 10], buf[0x40 + 11]); + fu_device_set_version (FU_DEVICE (self), version, FWUPD_VERSION_FORMAT_PAIR); + return TRUE; +} + +static gboolean +fu_rts54hid_device_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* disconnect, set config, reattach kernel driver */ + if (!g_usb_device_set_configuration (usb_device, 0x00, error)) + return FALSE; + if (!g_usb_device_claim_interface (usb_device, 0x00, /* HID */ + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to claim interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hid_device_setup (FuDevice *device, GError **error) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device); + + /* check this device is correct */ + if (!fu_rts54hid_device_ensure_status (self, error)) + return FALSE; + + /* both conditions must be set */ + if (!self->fw_auth) { + fu_device_set_update_error (device, + "device does not support authentication"); + } else if (!self->dual_bank) { + fu_device_set_update_error (device, + "device does not support dual-bank updating"); + } else { + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hid_device_close (FuUsbDevice *device, GError **error) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device); + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* set MCU to normal clock rate */ + if (!fu_rts54hid_device_set_clock_mode (self, FALSE, error)) + return FALSE; + + /* we're done here */ + if (!g_usb_device_release_interface (usb_device, 0x00, /* HID */ + 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_rts54hid_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device); + g_autoptr(GPtrArray) chunks = NULL; + + /* set MCU to high clock rate for better ISP performance */ + if (!fu_rts54hid_device_set_clock_mode (self, TRUE, error)) + return FALSE; + + /* erase spare flash bank only if it is not empty */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_rts54hid_device_erase_spare_bank (self, error)) + return FALSE; + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes (fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + FU_RTS54HID_TRANSFER_BLOCK_SIZE); + + /* write each block */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + + /* write chunk */ + if (!fu_rts54hid_device_write_flash (self, + chk->address, + chk->data, + chk->data_sz, + error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len * 2); + } + + /* get device to authenticate the firmware */ + if (!fu_rts54hid_device_verify_update_fw (self, error)) + return FALSE; + + /* send software reset to run available flash code */ + if (!fu_rts54hid_device_reset_to_flash (self, error)) + return FALSE; + + /* success! */ + return TRUE; +} + +static void +fu_rts54hid_device_init (FuRts54HidDevice *self) +{ +} + +static void +fu_rts54hid_device_class_init (FuRts54HidDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->write_firmware = fu_rts54hid_device_write_firmware; + klass_device->to_string = fu_rts54hid_device_to_string; + klass_device->setup = fu_rts54hid_device_setup; + klass_usb_device->open = fu_rts54hid_device_open; + klass_usb_device->close = fu_rts54hid_device_close; +} + +FuRts54HidDevice * +fu_rts54hid_device_new (FuUsbDevice *device) +{ + FuRts54HidDevice *self = g_object_new (FU_TYPE_RTS54HID_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff -Nru fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-device.h fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-device.h --- fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_RTS54HID_DEVICE (fu_rts54hid_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuRts54HidDevice, fu_rts54hid_device, FU, RTS54HID_DEVICE, FuUsbDevice) + +FuRts54HidDevice *fu_rts54hid_device_new (FuUsbDevice *device); +gboolean fu_rts54hid_device_set_report (FuRts54HidDevice *self, + guint8 *buf, + gsize buf_sz, + GError **error); +gboolean fu_rts54hid_device_get_report (FuRts54HidDevice *self, + guint8 *buf, + gsize buf_sz, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-module.c fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-module.c --- fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-module.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-module.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-chunk.h" +#include "fu-rts54hid-common.h" +#include "fu-rts54hid-module.h" +#include "fu-rts54hid-device.h" + +struct _FuRts54HidModule { + FuDevice parent_instance; + guint8 slave_addr; + guint8 i2c_speed; + guint8 register_addr_len; +}; + +G_DEFINE_TYPE (FuRts54HidModule, fu_rts54hid_module, FU_TYPE_DEVICE) + +static void +fu_rts54hid_module_to_string (FuDevice *module, GString *str) +{ + FuRts54HidModule *self = FU_RTS54HID_MODULE (module); + g_string_append (str, " FuRts54HidModule:\n"); + g_string_append_printf (str, " slave-addr: 0x%02x\n", self->slave_addr); + g_string_append_printf (str, " i2c-speed: 0x%02x\n", self->i2c_speed); + g_string_append_printf (str, " register_addr_len: 0x%02x\n", self->register_addr_len); +} + +static FuRts54HidDevice * +fu_rts54hid_module_get_parent (FuRts54HidModule *self, GError **error) +{ + FuDevice *parent = fu_device_get_parent (FU_DEVICE (self)); + if (parent == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no parent set"); + return NULL; + } + return FU_RTS54HID_DEVICE (parent); +} + +static gboolean +fu_rts54hid_module_i2c_write (FuRts54HidModule *self, + const guint8 *data, + guint8 data_sz, + GError **error) +{ + FuRts54HidDevice *parent; + const FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_I2C_WRITE, + .dwregaddr = 0, + .bufferlen = GUINT16_TO_LE (data_sz), + .parameters_i2c = {.slave_addr = self->slave_addr, + .data_sz = self->register_addr_len, + .speed = self->i2c_speed | 0x80}, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + + g_return_val_if_fail (data_sz <= 128, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data_sz != 0, FALSE); + + /* get parent to issue command */ + parent = fu_rts54hid_module_get_parent (self, error); + if (parent == NULL) + return FALSE; + + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + memcpy (buf + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, data, data_sz); + if (!fu_rts54hid_device_set_report (parent, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to write i2c @%04x: ", self->slave_addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_module_i2c_read (FuRts54HidModule *self, + guint32 cmd, + guint8 *data, + guint8 data_sz, + GError **error) +{ + FuRts54HidDevice *parent; + const FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_I2C_READ, + .dwregaddr = GUINT32_TO_LE (cmd), + .bufferlen = GUINT16_TO_LE (data_sz), + .parameters_i2c = {.slave_addr = self->slave_addr, + .data_sz = self->register_addr_len, + .speed = self->i2c_speed | 0x80}, + }; + guint8 buf[FU_RTS54HID_REPORT_LENGTH] = { 0 }; + + g_return_val_if_fail (data_sz <= 192, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data_sz != 0, FALSE); + + /* get parent to issue command */ + parent = fu_rts54hid_module_get_parent (self, error); + if (parent == NULL) + return FALSE; + + /* read from module */ + memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); + if (!fu_rts54hid_device_set_report (parent, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to write i2c @%04x: ", self->slave_addr); + return FALSE; + } + if (!fu_rts54hid_device_get_report (parent, buf, sizeof(buf), error)) + return FALSE; + memcpy (data, buf + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, data_sz); + + return TRUE; +} + +static gboolean +fu_rts54hid_module_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuRts54HidModule *self = FU_RTS54HID_MODULE (device); + + /* load slave address from quirks */ + if (g_strcmp0 (key, "Rts54SlaveAddr") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp <= 0xff) { + self->slave_addr = tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid slave address"); + return FALSE; + } + + /* load i2c speed from quirks */ + if (g_strcmp0 (key, "Rts54I2cSpeed") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp < FU_RTS54HID_I2C_SPEED_LAST) { + self->i2c_speed = tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid I²C speed"); + return FALSE; + } + + /* load register address length from quirks */ + if (g_strcmp0 (key, "Rts54RegisterAddrLen") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp <= 0xff) { + self->register_addr_len = tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid register address length"); + 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_rts54hid_module_open (FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent (device); + if (parent == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no parent device"); + return FALSE; + } + return fu_device_open (parent, error); +} + +static gboolean +fu_rts54hid_module_close (FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent (device); + if (parent == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no parent device"); + return FALSE; + } + return fu_device_close (parent, error); +} + +static gboolean +fu_rts54hid_module_write_firmware (FuDevice *module, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54HidModule *self = FU_RTS54HID_MODULE (module); + g_autoptr(GPtrArray) chunks = NULL; + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes (fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + FU_RTS54HID_TRANSFER_BLOCK_SIZE); + + if (0) { + if (!fu_rts54hid_module_i2c_read (self, 0x0000, NULL, 0, error)) + return FALSE; + if (!fu_rts54hid_module_i2c_write (self, NULL, 0, error)) + return FALSE; + } + + /* write each block */ + fu_device_set_status (module, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + + /* write chunk */ + if (!fu_rts54hid_module_i2c_write (self, + chk->data, + chk->data_sz, + error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (module, (gsize) i, (gsize) chunks->len * 2); + } + + /* success! */ + return TRUE; +} + +static void +fu_rts54hid_module_init (FuRts54HidModule *self) +{ +} + +static void +fu_rts54hid_module_class_init (FuRts54HidModuleClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->write_firmware = fu_rts54hid_module_write_firmware; + klass_device->to_string = fu_rts54hid_module_to_string; + klass_device->set_quirk_kv = fu_rts54hid_module_set_quirk_kv; + klass_device->open = fu_rts54hid_module_open; + klass_device->close = fu_rts54hid_module_close; +} + +FuRts54HidModule * +fu_rts54hid_module_new (void) +{ + FuRts54HidModule *self = NULL; + self = g_object_new (FU_TYPE_RTS54HID_MODULE, NULL); + return self; +} diff -Nru fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-module.h fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-module.h --- fwupd-1.0.9/plugins/rts54hid/fu-rts54hid-module.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/fu-rts54hid-module.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_RTS54HID_MODULE (fu_rts54hid_module_get_type ()) +G_DECLARE_FINAL_TYPE (FuRts54HidModule, fu_rts54hid_module, FU, RTS54HID_MODULE, FuDevice) + +FuRts54HidModule *fu_rts54hid_module_new (void); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/rts54hid/meson.build fwupd-1.2.10/plugins/rts54hid/meson.build --- fwupd-1.0.9/plugins/rts54hid/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,30 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginRts54Hid"'] + +install_data([ + 'rts54hid.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_rts54hid', + fu_hash, + sources : [ + 'fu-rts54hid-device.c', + 'fu-rts54hid-module.c', + 'fu-plugin-rts54hid.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 -Nru fwupd-1.0.9/plugins/rts54hid/README.md fwupd-1.2.10/plugins/rts54hid/README.md --- fwupd-1.0.9/plugins/rts54hid/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,44 @@ +Realtek RTS54HID HID Support +========================= + +Introduction +------------ + +This plugin allows the user to update any supported hub and attached downstream +ICs using a custom HID-based flashing protocol. It does not support any RTS54xx +device using the HUB update protocol. + +Other devices connected to the RTS54HIDxx using I2C will be supported soon. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format. + +This plugin supports the following protocol ID: + + * com.realtek.rts54 + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_0BDA&PID_1100&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 -Nru fwupd-1.0.9/plugins/rts54hid/rts54hid.quirk fwupd-1.2.10/plugins/rts54hid/rts54hid.quirk --- fwupd-1.0.9/plugins/rts54hid/rts54hid.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hid/rts54hid.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,16 @@ +# RTS5423 +[DeviceInstanceId=USB\VID_0BDA&PID_1100] +Plugin = rts54hid +FirmwareSizeMin = 0x10000 +FirmwareSizeMax = 0x40000 +Children = FuRts54HidModule|USB\VID_0BDA&PID_1100&I2C_01 + +# this is a fictitious example... +[DeviceInstanceId=USB\VID_0BDA&PID_1100&I2C_01] +Plugin = rts54hid +Name = HDMI Converter +Flags = updatable +FirmwareSize = 0x20000 +Rts54SlaveAddr = 0x00 +Rts54I2cSpeed = 0x00 +Rts54RegisterAddrLen = 0x04 diff -Nru fwupd-1.0.9/plugins/rts54hub/data/lsusb.txt fwupd-1.2.10/plugins/rts54hub/data/lsusb.txt --- fwupd-1.0.9/plugins/rts54hub/data/lsusb.txt 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hub/data/lsusb.txt 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,137 @@ +Bus 001 Device 038: ID 0bda:5423 Realtek Semiconductor Corp. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.10 + bDeviceClass 9 Hub + bDeviceSubClass 0 + bDeviceProtocol 2 TT per port + bMaxPacketSize0 64 + idVendor 0x0bda Realtek Semiconductor Corp. + idProduct 0x5423 + bcdDevice 1.19 + iManufacturer 1 Generic + iProduct 2 4-Port USB 2.0 Hub + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xe0 + Self Powered + Remote Wakeup + MaxPower 0mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 9 Hub + bInterfaceSubClass 0 + bInterfaceProtocol 1 Single TT + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0001 1x 1 bytes + bInterval 12 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 1 + bInterfaceClass 9 Hub + bInterfaceSubClass 0 + bInterfaceProtocol 2 TT per port + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0001 1x 1 bytes + bInterval 12 +Hub Descriptor: + bLength 9 + bDescriptorType 41 + nNbrPorts 5 + wHubCharacteristic 0x00a9 + Per-port power switching + Per-port overcurrent protection + TT think time 16 FS bits + Port indicators + bPwrOn2PwrGood 0 * 2 milli seconds + bHubContrCurrent 100 milli Ampere + DeviceRemovable 0x00 + PortPwrCtrlMask 0xff + Hub Port Status: + Port 1: 0000.0100 power + Port 2: 0000.0100 power + Port 3: 0000.0100 power + Port 4: 0000.0100 power + Port 5: 0000.0503 highspeed power enable connect +Binary Object Store Descriptor: + bLength 5 + bDescriptorType 15 + wTotalLength 73 + bNumDeviceCaps 5 + USB 2.0 Extension Device Capability: + bLength 7 + bDescriptorType 16 + bDevCapabilityType 2 + bmAttributes 0x0000f41e + BESL Link Power Management (LPM) Supported + BESL value 1024 us + Deep BESL value 61440 us + SuperSpeed USB Device Capability: + bLength 10 + bDescriptorType 16 + bDevCapabilityType 3 + bmAttributes 0x00 + wSpeedsSupported 0x000e + Device can operate at Full Speed (12Mbps) + Device can operate at High Speed (480Mbps) + Device can operate at SuperSpeed (5Gbps) + bFunctionalitySupport 1 + Lowest fully-functional device speed is Full Speed (12Mbps) + bU1DevExitLat 10 micro seconds + bU2DevExitLat 1023 micro seconds + SuperSpeedPlus USB Device Capability: + bLength 28 + bDescriptorType 16 + bDevCapabilityType 10 + bmAttributes 0x00000023 + Sublink Speed Attribute count 3 + Sublink Speed ID count 1 + wFunctionalitySupport 0x1100 + bmSublinkSpeedAttr[0] 0x00050030 + Speed Attribute ID: 0 5Gb/s Symmetric RX SuperSpeed + bmSublinkSpeedAttr[1] 0x000500b0 + Speed Attribute ID: 0 5Gb/s Symmetric TX SuperSpeed + bmSublinkSpeedAttr[2] 0x000a4031 + Speed Attribute ID: 1 10Gb/s Symmetric RX SuperSpeedPlus + bmSublinkSpeedAttr[3] 0x000a40b1 + Speed Attribute ID: 1 10Gb/s Symmetric TX SuperSpeedPlus + Container ID Device Capability: + bLength 20 + bDescriptorType 16 + bDevCapabilityType 4 + bReserved 0 + ContainerID {20b9cde5-7039-e011-a935-0002a5d5c51b} + ** UNRECOGNIZED: 03 10 0b +Device Status: 0x0001 + Self Powered diff -Nru fwupd-1.0.9/plugins/rts54hub/fu-plugin-rts54hub.c fwupd-1.2.10/plugins/rts54hub/fu-plugin-rts54hub.c --- fwupd-1.0.9/plugins/rts54hub/fu-plugin-rts54hub.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hub/fu-plugin-rts54hub.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-rts54hub-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.realtek.rts54"); +} + +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, flags, error); +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuRts54HubDevice) dev = NULL; + + /* open the device */ + dev = fu_rts54hub_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + + /* success */ + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/rts54hub/fu-rts54hub-device.c fwupd-1.2.10/plugins/rts54hub/fu-rts54hub-device.c --- fwupd-1.0.9/plugins/rts54hub/fu-rts54hub-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hub/fu-rts54hub-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-chunk.h" +#include "fu-rts54hub-device.h" + +struct _FuRts54HubDevice { + FuUsbDevice parent_instance; + gboolean fw_auth; + gboolean dual_bank; + gboolean running_on_flash; + guint8 vendor_cmd; +}; + +G_DEFINE_TYPE (FuRts54HubDevice, fu_rts54hub_device, FU_TYPE_USB_DEVICE) + +#define FU_RTS54HUB_DEVICE_TIMEOUT 100 /* ms */ +#define FU_RTS54HUB_DEVICE_TIMEOUT_RW 1000 /* ms */ +#define FU_RTS54HUB_DEVICE_TIMEOUT_ERASE 5000 /* ms */ +#define FU_RTS54HUB_DEVICE_TIMEOUT_AUTH 10000 /* ms */ +#define FU_RTS54HUB_DEVICE_BLOCK_SIZE 4096 +#define FU_RTS54HUB_DEVICE_STATUS_LEN 25 + +typedef enum { + FU_RTS54HUB_VENDOR_CMD_NONE = 0x00, + FU_RTS54HUB_VENDOR_CMD_STATUS = 1 << 0, + FU_RTS54HUB_VENDOR_CMD_FLASH = 1 << 1, +} FuRts54HubVendorCmd; + +static void +fu_rts54hub_device_to_string (FuDevice *device, GString *str) +{ + FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device); + g_string_append (str, " FuRts54HubDevice:\n"); + g_string_append_printf (str, " fw-auth: %i\n", self->fw_auth); + g_string_append_printf (str, " dual-bank: %i\n", self->dual_bank); + g_string_append_printf (str, " running-on-flash: %i\n", self->running_on_flash); +} + +static gboolean +fu_rts54hub_device_highclockmode (FuRts54HubDevice *self, guint16 value, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0x06, /* request */ + value, /* value */ + 0, /* idx */ + NULL, 0, /* data */ + NULL, /* actual */ + FU_RTS54HUB_DEVICE_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "failed to set highclockmode: "); + return FALSE; + } + return TRUE; +} + + +static gboolean +fu_rts54hub_device_reset_flash (FuRts54HubDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xC0 + 0x29, /* request */ + 0x0, /* value */ + 0x0, /* idx */ + NULL, 0, /* data */ + NULL, /* actual */ + FU_RTS54HUB_DEVICE_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "failed to reset flash: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hub_device_write_flash (FuRts54HubDevice *self, + guint32 addr, + const guint8 *data, + gsize datasz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_len = 0; + g_autofree guint8 *datarw = g_memdup (data, datasz); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xC0 + 0x08, /* request */ + addr % (1 << 16), /* value */ + addr / (1 << 16), /* idx */ + datarw, datasz, /* data */ + &actual_len, + FU_RTS54HUB_DEVICE_TIMEOUT_RW, + NULL, error)) { + g_prefix_error (error, "failed to write flash: "); + return FALSE; + } + if (actual_len != datasz) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "only wrote %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + return TRUE; +} + +#if 0 +static gboolean +fu_rts54hub_device_read_flash (FuRts54HubDevice *self, + guint32 addr, + guint8 *data, + gsize datasz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_len = 0; + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xC0 + 0x18, /* request */ + addr % (1 << 16), /* value */ + addr / (1 << 16), /* idx */ + data, datasz, /* data */ + &actual_len, + FU_RTS54HUB_DEVICE_TIMEOUT_RW, + NULL, error)) { + g_prefix_error (error, "failed to read flash: "); + return FALSE; + } + if (actual_len != datasz) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "only read %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + return TRUE; +} +#endif + +static gboolean +fu_rts54hub_device_flash_authentication (FuRts54HubDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xC0 + 0x19, /* request */ + 0x01, /* value */ + 0x0, /* idx */ + NULL, 0, /* data */ + NULL, /* actual */ + FU_RTS54HUB_DEVICE_TIMEOUT_AUTH, + NULL, error)) { + g_prefix_error (error, "failed to authenticate: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hub_device_erase_flash (FuRts54HubDevice *self, + guint8 erase_type, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xC0 + 0x28, /* request */ + erase_type * 256, /* value */ + 0x0, /* idx */ + NULL, 0, /* data */ + NULL, /* actual */ + FU_RTS54HUB_DEVICE_TIMEOUT_ERASE, + NULL, error)) { + g_prefix_error (error, "failed to erase flash: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hub_device_vendor_cmd (FuRts54HubDevice *self, guint8 value, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + + /* don't set something that's already set */ + if (self->vendor_cmd == value) { + g_debug ("skipping vendor command 0x%02x as already set", value); + return TRUE; + } + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0x02, /* request */ + value, /* value */ + 0x0bda, /* idx */ + NULL, 0, /* data */ + NULL, /* actual */ + FU_RTS54HUB_DEVICE_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "failed to issue vendor cmd 0x%02x: ", value); + return FALSE; + } + self->vendor_cmd = value; + return TRUE; +} + +static gboolean +fu_rts54hub_device_ensure_status (FuRts54HubDevice *self, GError **error) +{ + guint8 data[FU_RTS54HUB_DEVICE_STATUS_LEN] = { 0 }; + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_len = 0; + + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0x09, /* request */ + 0x0, /* value */ + 0x0, /* idx */ + data, sizeof(data), + &actual_len, /* actual */ + FU_RTS54HUB_DEVICE_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "failed to get status: "); + return FALSE; + } + if (actual_len != FU_RTS54HUB_DEVICE_STATUS_LEN) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "only read %" G_GSIZE_FORMAT "bytes", actual_len); + return FALSE; + } + + /* check the hardware capabilities */ + self->dual_bank = (data[7] & 0x80) == 0x80; + self->fw_auth = (data[13] & 0x02) > 0; + self->running_on_flash = (data[15] & 0x02) > 0; + + return TRUE; +} + +static gboolean +fu_rts54hub_device_setup (FuDevice *device, GError **error) +{ + FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device); + + /* check this device is correct */ + if (!fu_rts54hub_device_vendor_cmd (self, FU_RTS54HUB_VENDOR_CMD_STATUS, error)) { + g_prefix_error (error, "failed to vendor enable: "); + return FALSE; + } + if (!fu_rts54hub_device_ensure_status (self, error)) + return FALSE; + + /* all three conditions must be set */ + if (!self->running_on_flash) { + fu_device_set_update_error (device, + "device is abnormally running from ROM"); + } else if (!self->fw_auth) { + fu_device_set_update_error (device, + "device does not support authentication"); + } else if (!self->dual_bank) { + fu_device_set_update_error (device, + "device does not support dual-bank updating"); + } else { + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_device_close (FuUsbDevice *device, GError **error) +{ + FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device); + + /* disable vendor commands */ + if (self->vendor_cmd != FU_RTS54HUB_VENDOR_CMD_NONE) { + if (!fu_rts54hub_device_vendor_cmd (self, FU_RTS54HUB_VENDOR_CMD_NONE, error)) { + g_prefix_error (error, "failed to disable vendor command: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device); + g_autoptr(GPtrArray) chunks = NULL; + + /* enable vendor commands */ + if (!fu_rts54hub_device_vendor_cmd (self, + FU_RTS54HUB_VENDOR_CMD_STATUS | + FU_RTS54HUB_VENDOR_CMD_FLASH, + error)) { + g_prefix_error (error, "failed to cmd enable: "); + return FALSE; + } + + /* erase spare flash bank only if it is not empty */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_rts54hub_device_erase_flash (self, 1, error)) + return FALSE; + + /* set MCU clock to high clock mode */ + if (!fu_rts54hub_device_highclockmode (self, 0x0001, error)) { + g_prefix_error (error, "failed to enable MCU clock: "); + return FALSE; + } + + /* set SPI controller clock to high clock mode */ + if (!fu_rts54hub_device_highclockmode (self, 0x0101, error)) { + g_prefix_error (error, "failed to enable SPI clock: "); + return FALSE; + } + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes (fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + FU_RTS54HUB_DEVICE_BLOCK_SIZE); + + /* write each block */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + + /* write chunk */ + if (!fu_rts54hub_device_write_flash (self, + chk->address, + chk->data, + chk->data_sz, + error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len - 1); + } + + /* get device to authenticate the firmware */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY); + if (!fu_rts54hub_device_flash_authentication (self, error)) + return FALSE; + + /* send software reset to run available flash code */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!fu_rts54hub_device_reset_flash (self, error)) + return FALSE; + + /* don't reset the vendor command enable, the device will be rebooted */ + self->vendor_cmd = FU_RTS54HUB_VENDOR_CMD_NONE; + + /* success! */ + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static GBytes * +fu_rts54hub_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + gsize sz = 0; + const guint8 *data = g_bytes_get_data (fw, &sz); + if (sz < 0x7ef3) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware was too small"); + return NULL; + } + if ((data[0x7ef3] & 0xf0) != 0x80) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware needs to be dual bank"); + return NULL; + } + return g_bytes_ref (fw); +} + +static void +fu_rts54hub_device_init (FuRts54HubDevice *self) +{ + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_rts54hub_device_class_init (FuRts54HubDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->write_firmware = fu_rts54hub_device_write_firmware; + klass_device->setup = fu_rts54hub_device_setup; + klass_device->to_string = fu_rts54hub_device_to_string; + klass_device->prepare_firmware = fu_rts54hub_device_prepare_firmware; + klass_usb_device->close = fu_rts54hub_device_close; +} + +FuRts54HubDevice * +fu_rts54hub_device_new (FuUsbDevice *device) +{ + FuRts54HubDevice *self = g_object_new (FU_TYPE_RTS54HUB_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff -Nru fwupd-1.0.9/plugins/rts54hub/fu-rts54hub-device.h fwupd-1.2.10/plugins/rts54hub/fu-rts54hub-device.h --- fwupd-1.0.9/plugins/rts54hub/fu-rts54hub-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hub/fu-rts54hub-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_RTS54HUB_DEVICE (fu_rts54hub_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuRts54HubDevice, fu_rts54hub_device, FU, RTS54HUB_DEVICE, FuUsbDevice) + +FuRts54HubDevice *fu_rts54hub_device_new (FuUsbDevice *device); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/rts54hub/meson.build fwupd-1.2.10/plugins/rts54hub/meson.build --- fwupd-1.0.9/plugins/rts54hub/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hub/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,29 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginRts54Hub"'] + +install_data([ + 'rts54hub.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_rts54hub', + fu_hash, + sources : [ + 'fu-rts54hub-device.c', + 'fu-plugin-rts54hub.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 -Nru fwupd-1.0.9/plugins/rts54hub/README.md fwupd-1.2.10/plugins/rts54hub/README.md --- fwupd-1.0.9/plugins/rts54hub/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hub/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,30 @@ +Realtek RTS54 HUB Support +========================= + +Introduction +------------ + +This plugin allows the user to update any supported hub and attached downstream +ICs using a custom HUB-based flashing protocol. It does not support any RTS54xx +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 -Nru fwupd-1.0.9/plugins/rts54hub/rts54hub.quirk fwupd-1.2.10/plugins/rts54hub/rts54hub.quirk --- fwupd-1.0.9/plugins/rts54hub/rts54hub.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/rts54hub/rts54hub.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,5 @@ +# RTS5423 Development Board +[DeviceInstanceId=USB\VID_0BDA&PID_5423] +Plugin = rts54hub +FirmwareSizeMin = 0x20000 +FirmwareSizeMax = 0x40000 diff -Nru fwupd-1.0.9/plugins/steelseries/fu-plugin-steelseries.c fwupd-1.2.10/plugins/steelseries/fu-plugin-steelseries.c --- fwupd-1.0.9/plugins/steelseries/fu-plugin-steelseries.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/steelseries/fu-plugin-steelseries.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,20 +6,26 @@ #include "config.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" #include "fu-steelseries-device.h" +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); +} + gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) { - g_autoptr(FuSteelseriesDevice) device = NULL; + g_autoptr(FuSteelseriesDevice) dev = NULL; g_autoptr(FuDeviceLocker) locker = NULL; - device = fu_steelseries_device_new (usb_device); - locker = fu_device_locker_new (device, error); + dev = fu_steelseries_device_new (device); + locker = fu_device_locker_new (dev, error); if (locker == NULL) return FALSE; - fu_plugin_device_add (plugin, FU_DEVICE (device)); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } diff -Nru fwupd-1.0.9/plugins/steelseries/fu-steelseries-device.c fwupd-1.2.10/plugins/steelseries/fu-steelseries-device.c --- fwupd-1.0.9/plugins/steelseries/fu-steelseries-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/steelseries/fu-steelseries-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -16,47 +15,32 @@ G_DEFINE_TYPE (FuSteelseriesDevice, fu_steelseries_device, FU_TYPE_USB_DEVICE) static gboolean -fu_steelseries_device_probe (FuUsbDevice *device, GError **error) +fu_steelseries_device_open (FuUsbDevice *device, GError **error) { GUsbDevice *usb_device = fu_usb_device_get_dev (device); + const guint8 iface_idx = 0x00; - /* not the right kind of device */ - if (g_usb_device_get_vid (usb_device) != 0x1038 || - g_usb_device_get_pid (usb_device) != 0x1702) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported with this device"); + /* get firmware version on SteelSeries Rival 100 */ + if (!g_usb_device_claim_interface (usb_device, iface_idx, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to claim interface: "); return FALSE; } - /* hardcoded */ - fu_device_set_name (FU_DEVICE (device), "SteelSeries Rival 100"); - fu_device_set_vendor (FU_DEVICE (device), "SteelSeries"); - fu_device_set_summary (FU_DEVICE (device), "An optical gaming mouse"); - fu_device_add_icon (FU_DEVICE (device), "input-mouse"); - /* success */ return TRUE; } static gboolean -fu_steelseries_device_open (FuUsbDevice *device, GError **error) +fu_steelseries_device_setup (FuDevice *device, GError **error) { - GUsbDevice *usb_device = fu_usb_device_get_dev (device); - const guint8 iface_idx = 0x00; + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); gboolean ret; gsize actual_len = 0; guint8 data[32]; g_autofree gchar *version = NULL; - /* get firmware version on SteelSeries Rival 100 */ - if (!g_usb_device_claim_interface (usb_device, iface_idx, - G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, - error)) { - g_prefix_error (error, "failed to claim interface: "); - return FALSE; - } memset (data, 0x00, sizeof(data)); data[0] = 0x16; ret = g_usb_device_control_transfer (usb_device, @@ -103,7 +87,7 @@ return FALSE; } version = g_strdup_printf ("%i.%i.%i", data[0], data[1], data[2]); - fu_device_set_version (FU_DEVICE (device), version); + fu_device_set_version (FU_DEVICE (device), version, FWUPD_VERSION_FORMAT_TRIPLET); /* success */ return TRUE; @@ -135,18 +119,17 @@ static void fu_steelseries_device_class_init (FuSteelseriesDeviceClass *klass) { + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->setup = fu_steelseries_device_setup; klass_usb_device->open = fu_steelseries_device_open; klass_usb_device->close = fu_steelseries_device_close; - klass_usb_device->probe = fu_steelseries_device_probe; } FuSteelseriesDevice * -fu_steelseries_device_new (GUsbDevice *usb_device) +fu_steelseries_device_new (FuUsbDevice *device) { - FuSteelseriesDevice *device = NULL; - device = g_object_new (FU_TYPE_STEELSERIES_DEVICE, - "usb-device", usb_device, - NULL); - return device; + FuSteelseriesDevice *self = g_object_new (FU_TYPE_STEELSERIES_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; } diff -Nru fwupd-1.0.9/plugins/steelseries/fu-steelseries-device.h fwupd-1.2.10/plugins/steelseries/fu-steelseries-device.h --- fwupd-1.0.9/plugins/steelseries/fu-steelseries-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/steelseries/fu-steelseries-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_STEELSERIES_DEVICE_H -#define __FU_STEELSERIES_DEVICE_H - -#include -#include +#pragma once #include "fu-plugin.h" @@ -23,8 +18,6 @@ FuUsbDeviceClass parent_class; }; -FuSteelseriesDevice *fu_steelseries_device_new (GUsbDevice *usb_device); +FuSteelseriesDevice *fu_steelseries_device_new (FuUsbDevice *device); G_END_DECLS - -#endif /* __FU_STEELSERIES_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/steelseries/meson.build fwupd-1.2.10/plugins/steelseries/meson.build --- fwupd-1.0.9/plugins/steelseries/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/steelseries/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,11 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginSteelSeries"'] +install_data(['steelseries.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_steelseries', + fu_hash, sources : [ 'fu-plugin-steelseries.c', 'fu-steelseries-device.c', @@ -12,6 +17,9 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff -Nru fwupd-1.0.9/plugins/steelseries/README.md fwupd-1.2.10/plugins/steelseries/README.md --- fwupd-1.0.9/plugins/steelseries/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/steelseries/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -7,3 +7,12 @@ 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 -Nru fwupd-1.0.9/plugins/steelseries/steelseries.quirk fwupd-1.2.10/plugins/steelseries/steelseries.quirk --- fwupd-1.0.9/plugins/steelseries/steelseries.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/steelseries/steelseries.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,5 @@ +# Rival 100 +[DeviceInstanceId=USB\VID_1038&PID_1702] +Plugin = steelseries +Summary = An optical gaming mouse +Icon = input-mouse diff -Nru fwupd-1.0.9/plugins/superio/fu-plugin-superio.c fwupd-1.2.10/plugins/superio/fu-plugin-superio.c --- fwupd-1.0.9/plugins/superio/fu-plugin-superio.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-plugin-superio.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-superio-it85-device.h" +#include "fu-superio-it89-device.h" + +#define FU_QUIRKS_SUPERIO_CHIPSETS "SuperioChipsets" + +static gboolean +fu_plugin_superio_coldplug_chipset (FuPlugin *plugin, const gchar *chipset, GError **error) +{ + g_autoptr(FuSuperioDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autofree gchar *key = g_strdup_printf ("SuperIO=%s", chipset); + guint64 id; + guint64 port; + + /* get ID we need for the chipset */ + id = fu_plugin_lookup_quirk_by_id_as_uint64 (plugin, key, "Id"); + if (id == 0x0000 || id > 0xffff) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "SuperIO chip %s has invalid Id", chipset); + return FALSE; + } + + /* 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 Port", chipset); + return FALSE; + } + + /* create IT89xx or IT89xx */ + if (id >> 8 == 0x85) { + dev = g_object_new (FU_TYPE_SUPERIO_IT85_DEVICE, + "chipset", chipset, + "id", id, + "port", port, + NULL); + } else if (id >> 8 == 0x89) { + dev = g_object_new (FU_TYPE_SUPERIO_IT89_DEVICE, + "chipset", chipset, + "id", id, + "port", port, + NULL); + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "SuperIO chip %s has unsupported Id", chipset); + return FALSE; + } + + /* unlock */ + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + + return TRUE; +} + +static gboolean +fu_plugin_superio_coldplug_chipsets (FuPlugin *plugin, const gchar *str, GError **error) +{ + g_auto(GStrv) chipsets = g_strsplit (str, ",", -1); + for (guint i = 0; chipsets[i] != NULL; i++) { + if (!fu_plugin_superio_coldplug_chipset (plugin, chipsets[i], error)) + return FALSE; + } + return TRUE; +} + +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, "tw.com.ite.superio"); +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + GPtrArray *hwids = fu_plugin_get_hwids (plugin); + for (guint i = 0; i < hwids->len; i++) { + const gchar *tmp; + const gchar *guid = g_ptr_array_index (hwids, i); + g_autofree gchar *key = g_strdup_printf ("HwId=%s", guid); + tmp = fu_plugin_lookup_quirk_by_id (plugin, key, FU_QUIRKS_SUPERIO_CHIPSETS); + if (tmp == NULL) + continue; + if (!fu_plugin_superio_coldplug_chipsets (plugin, tmp, error)) + return FALSE; + } + return TRUE; +} + +gboolean +fu_plugin_verify_attach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_attach (device, error); +} + +gboolean +fu_plugin_verify_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_verify (FuPlugin *plugin, FuDevice *device, + FuPluginVerifyFlags flags, GError **error) +{ + g_autoptr(GBytes) fw = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + GChecksumType checksum_types[] = { + G_CHECKSUM_SHA1, + G_CHECKSUM_SHA256, + 0 }; + + /* get data */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + fw = fu_device_read_firmware (device, error); + if (fw == NULL) + return FALSE; + for (guint i = 0; checksum_types[i] != 0; i++) { + g_autofree gchar *hash = NULL; + hash = g_compute_checksum_for_bytes (checksum_types[i], fw); + fu_device_add_checksum (device, hash); + } + return TRUE; +} + +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_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, flags, error); +} diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-common.c fwupd-1.2.10/plugins/superio/fu-superio-common.c --- fwupd-1.0.9/plugins/superio/fu-superio-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#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; +} + +const gchar * +fu_superio_ldn_to_text (guint8 ldn) +{ + if (ldn == SIO_LDN_FDC) + return "Floppy Disk Controller"; + if (ldn == SIO_LDN_GPIO) + return "General Purpose IO"; + if (ldn == SIO_LDN_PARALLEL_PORT) + return "Parallel Port"; + if (ldn == SIO_LDN_UART1) + return "Serial Port 1"; + if (ldn == SIO_LDN_UART2) + return "Serial Port 2"; + if (ldn == SIO_LDN_UART3) + return "Serial Port 3"; + if (ldn == SIO_LDN_UART4) + return "Serial Port 4"; + if (ldn == SIO_LDN_SWUC) + return "System Wake-Up Control"; + if (ldn == SIO_LDN_KBC_MOUSE) + return "KBC/Mouse"; + if (ldn == SIO_LDN_KBC_KEYBOARD) + return "KBC/Keyboard"; + if (ldn == SIO_LDN_CIR) + return "Consumer IR"; + if (ldn == SIO_LDN_SMFI) + return "Shared Memory/Flash"; + if (ldn == SIO_LDN_RTCT) + return "RTC-like Timer"; + if (ldn == SIO_LDN_SSSP1) + return "Serial Peripheral"; + if (ldn == SIO_LDN_PECI) + return "Platform Environmental Control"; + if (ldn == SIO_LDN_PM1) + return "Power Management 1"; + if (ldn == SIO_LDN_PM2) + return "Power Management 2"; + if (ldn == SIO_LDN_PM3) + return "Power Management 3"; + if (ldn == SIO_LDN_PM4) + return "Power Management 4"; + if (ldn == SIO_LDN_PM5) + return "Power Management 5"; + return NULL; +} diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-common.h fwupd-1.2.10/plugins/superio/fu-superio-common.h --- fwupd-1.0.9/plugins/superio/fu-superio-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +/* for all LDNs */ +#define SIO_LDNxx_IDX_LDNSEL 0x07 +#define SIO_LDNxx_IDX_CHIPID1 0x20 +#define SIO_LDNxx_IDX_CHIPID2 0x21 +#define SIO_LDNxx_IDX_CHIPVER 0x22 +#define SIO_LDNxx_IDX_SIOCTRL 0x23 +#define SIO_LDNxx_IDX_SIOIRQ 0x25 +#define SIO_LDNxx_IDX_SIOGP 0x26 +#define SIO_LDNxx_IDX_SIOPWR 0x2d +#define SIO_LDNxx_IDX_D2ADR 0x2e +#define SIO_LDNxx_IDX_D2DAT 0x2f + +#define SIO_LDNxx_IDX_IOBAD0 0x60 /* 16 bit */ +#define SIO_LDNxx_IDX_IOBAD1 0x62 /* 16 bit */ + +/* these registers are only accessible by EC */ +#define GCTRL_ECHIPID1 0x2000 +#define GCTRL_ECHIPID2 0x2001 +#define GCTRL_ECHIPVER 0x2002 + +/* to create sub-addresses */ +#define SIO_DEPTH2_I2EC_ADDRL 0x10 +#define SIO_DEPTH2_I2EC_ADDRH 0x11 +#define SIO_DEPTH2_I2EC_DATA 0x12 + +/* + * The PMC is a communication channel used between the host and the EC. + * Compatible mode uses four registers: + * + * Name | EC | HOST | ADDR + * _____________________|_______________|_______________|______ + * PMDIR | RO | WO | 0x62 + * PMDOR | WO | RO | 0x62 + * PMCMDR | RO | RO | 0x66 + * PMSTR | RO | RO | 0x66 + */ +#define SIO_EC_PMC_PM1STS 0x00 +#define SIO_EC_PMC_PM1DO 0x01 +#define SIO_EC_PMC_PM1DOSCI 0x02 +#define SIO_EC_PMC_PM1DOCMI 0x03 +#define SIO_EC_PMC_PM1DI 0x04 +#define SIO_EC_PMC_PM1DISCI 0x05 +#define SIO_EC_PMC_PM1CTL 0x06 +#define SIO_EC_PMC_PM1IC 0x07 +#define SIO_EC_PMC_PM1IE 0x08 + +/* SPI commands */ +#define SIO_SPI_CMD_READ 0x03 +#define SIO_SPI_CMD_HS_READ 0x0b +#define SIO_SPI_CMD_FAST_READ_DUAL_OP 0x3b +#define SIO_SPI_CMD_FAST_READ_DUAL_IO 0xbb +#define SIO_SPI_CMD_4K_SECTOR_ERASE 0xd7 /* or 0x20 or 0x52 */ +#define SIO_SPI_CMD_64K_BLOCK_ERASE 0xd8 +#define SIO_SPI_CMD_CHIP_ERASE 0xc7 /* or 0x60 */ +#define SIO_SPI_CMD_PAGE_PROGRAM 0x02 +#define SIO_SPI_CMD_WRITE_WORD 0xad +#define SIO_SPI_CMD_RDSR 0x05 /* read status register */ +#define SIO_SPI_CMD_WRSR 0x01 /* write status register */ +#define SIO_SPI_CMD_WREN 0x06 /* write enable */ +#define SIO_SPI_CMD_WRDI 0x04 /* write disable */ +#define SIO_SPI_CMD_RDID 0xab +#define SIO_SPI_CMD_JEDEC_ID 0x9f +#define SIO_SPI_CMD_DPD 0xb9 /* deep sleep */ +#define SIO_SPI_CMD_RDPD 0xab /* wake from deep sleep */ + +/* EC Status Register (see ec/google/chromeec/ec_commands.h) */ +#define SIO_STATUS_EC_OBF (1 << 0) /* o/p buffer full */ +#define SIO_STATUS_EC_IBF (1 << 1) /* i/p buffer full */ +#define SIO_STATUS_EC_IS_BUSY (1 << 2) +#define SIO_STATUS_EC_IS_CMD (1 << 3) +#define SIO_STATUS_EC_BURST_ENABLE (1 << 4) +#define SIO_STATUS_EC_SCI (1 << 5) /* 1 if more events in queue */ + +/* EC Command Register (see KB3700-ds-01.pdf) */ +#define SIO_CMD_EC_READ 0x80 +#define SIO_CMD_EC_WRITE 0x81 +#define SIO_CMD_EC_BURST_ENABLE 0x82 +#define SIO_CMD_EC_BURST_DISABLE 0x83 +#define SIO_CMD_EC_QUERY_EVENT 0x84 +#define SIO_CMD_EC_GET_NAME_STR 0x92 +#define SIO_CMD_EC_GET_VERSION_STR 0x93 +#define SIO_CMD_EC_DISABLE_HOST_WA 0xdc +#define SIO_CMD_EC_ENABLE_HOST_WA 0xfc + +typedef enum { + SIO_LDN_FDC = 0x00, /* IT87 */ + SIO_LDN_UART1 = 0x01, /* IT87+IT89 */ + SIO_LDN_UART2 = 0x02, /* IT87+IT89 */ + SIO_LDN_PARALLEL_PORT = 0x03, /* IT87 */ + SIO_LDN_SWUC = 0x04, /* IT87+IT89 */ + SIO_LDN_KBC_MOUSE = 0x05, /* IT87+IT89 */ + SIO_LDN_KBC_KEYBOARD = 0x06, /* IT87+IT89 */ + SIO_LDN_GPIO = 0x07, /* IT87 */ + SIO_LDN_UART3 = 0x08, /* IT87 */ + SIO_LDN_UART4 = 0x09, /* IT87 */ + SIO_LDN_CIR = 0x0a, /* IT89 */ + SIO_LDN_SMFI = 0x0f, /* IT89 */ + SIO_LDN_RTCT = 0x10, /* IT89 */ + SIO_LDN_PM1 = 0x11, /* IT89 */ + SIO_LDN_PM2 = 0x12, /* IT89 */ + SIO_LDN_SSSP1 = 0x13, /* IT89 */ + SIO_LDN_PECI = 0x14, /* IT89 */ + SIO_LDN_PM3 = 0x17, /* IT89 */ + SIO_LDN_PM4 = 0x18, /* IT89 */ + SIO_LDN_PM5 = 0x19, /* IT89 */ + SIO_LDN_LAST = 0x1a +} SioLdn; + +const gchar *fu_superio_ldn_to_text (guint8 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); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-device.c fwupd-1.2.10/plugins/superio/fu-superio-device.c --- fwupd-1.0.9/plugins/superio/fu-superio-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include + +#include + +#include "fu-superio-common.h" +#include "fu-superio-device.h" + +#define FU_PLUGIN_SUPERIO_TIMEOUT 0.25 /* s */ + +typedef struct +{ + gint fd; + gchar *chipset; + guint16 port; + guint16 pm1_iobad0; + guint16 pm1_iobad1; + guint16 id; +} FuSuperioDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuSuperioDevice, fu_superio_device, FU_TYPE_DEVICE) + +#define GET_PRIVATE(o) (fu_superio_device_get_instance_private (o)) + +enum { + PROP_0, + PROP_CHIPSET, + PROP_PORT, + PROP_ID, + PROP_LAST +}; + +gboolean +fu_superio_device_regval (FuSuperioDevice *self, guint8 addr, + guint8 *data, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + if (!fu_superio_outb (priv->fd, priv->port, addr, error)) + return FALSE; + if (!fu_superio_inb (priv->fd, priv->port + 1, data, error)) + return FALSE; + return TRUE; +} + +gboolean +fu_superio_device_regval16 (FuSuperioDevice *self, guint8 addr, + guint16 *data, GError **error) +{ + guint8 msb; + guint8 lsb; + if (!fu_superio_device_regval (self, addr, &msb, error)) + return FALSE; + if (!fu_superio_device_regval (self, addr + 1, &lsb, error)) + return FALSE; + *data = ((guint16) msb << 8) | (guint16) lsb; + return TRUE; +} + +gboolean +fu_superio_device_regwrite (FuSuperioDevice *self, guint8 addr, + guint8 data, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + if (!fu_superio_outb (priv->fd, priv->port, addr, error)) + return FALSE; + if (!fu_superio_outb (priv->fd, priv->port + 1, data, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_superio_device_set_ldn (FuSuperioDevice *self, guint8 ldn, GError **error) +{ + return fu_superio_device_regwrite (self, SIO_LDNxx_IDX_LDNSEL, ldn, error); +} + +static gboolean +fu_superio_device_regdump (FuSuperioDevice *self, guint8 ldn, GError **error) +{ + const gchar *ldnstr = fu_superio_ldn_to_text (ldn); + guint8 buf[0xff] = { 0x00 }; + guint16 iobad0 = 0x0; + guint16 iobad1 = 0x0; + g_autoptr(GString) str = g_string_new (NULL); + + /* set LDN */ + if (!fu_superio_device_set_ldn (self, ldn, error)) + return FALSE; + for (guint i = 0x00; i < 0xff; i++) { + if (!fu_superio_device_regval (self, i, &buf[i], error)) + return FALSE; + } + + /* get the i/o base addresses */ + if (!fu_superio_device_regval16 (self, SIO_LDNxx_IDX_IOBAD0, &iobad0, error)) + return FALSE; + if (!fu_superio_device_regval16 (self, SIO_LDNxx_IDX_IOBAD1, &iobad1, error)) + return FALSE; + + g_string_append_printf (str, "LDN:0x%02x ", ldn); + if (iobad0 != 0x0) + g_string_append_printf (str, "IOBAD0:0x%04x ", iobad0); + if (iobad1 != 0x0) + g_string_append_printf (str, "IOBAD1:0x%04x ", iobad1); + if (ldnstr != NULL) + g_string_append_printf (str, "(%s)", ldnstr); + fu_common_dump_raw (G_LOG_DOMAIN, str->str, buf, sizeof(buf)); + return TRUE; +} + +static void +fu_superio_device_to_string (FuDevice *device, GString *str) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + g_string_append (str, " FuSuperioDevice:\n"); + g_string_append_printf (str, " fd:\t\t\t%i\n", priv->fd); + g_string_append_printf (str, " chipset:\t\t%s\n", priv->chipset); + g_string_append_printf (str, " id:\t\t\t0x%04x\n", (guint) priv->id); + g_string_append_printf (str, " port:\t\t0x%04x\n", (guint) priv->port); + g_string_append_printf (str, " pm1-iobad0:\t\t0x%04x\n", (guint) priv->pm1_iobad0); + g_string_append_printf (str, " pm1-iobad1:\t\t0x%04x\n", (guint) priv->pm1_iobad1); +} + +static guint16 +fu_superio_device_check_id (FuSuperioDevice *self, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + guint16 id_tmp; + + /* check ID, which can be done from any LDN */ + if (!fu_superio_device_regval16 (self, SIO_LDNxx_IDX_CHIPID1, &id_tmp, error)) + return FALSE; + if (priv->id != id_tmp) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "SuperIO chip not supported, got %04x, expected %04x", + (guint) id_tmp, (guint) priv->id); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_superio_device_wait_for (FuSuperioDevice *self, guint8 mask, gboolean set, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GTimer) timer = g_timer_new (); + do { + guint8 status = 0x00; + if (!fu_superio_inb (priv->fd, priv->pm1_iobad1, &status, error)) + return FALSE; + if (g_timer_elapsed (timer, NULL) > FU_PLUGIN_SUPERIO_TIMEOUT) + break; + if (set && (status & mask) != 0) + return TRUE; + if (!set && (status & mask) == 0) + return TRUE; + } while (TRUE); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "timed out whilst waiting for 0x%02x:%i", mask, set); + return FALSE; +} + +gboolean +fu_superio_device_ec_read (FuSuperioDevice *self, guint8 *data, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + if (!fu_superio_device_wait_for (self, SIO_STATUS_EC_OBF, TRUE, error)) + return FALSE; + return fu_superio_inb (priv->fd, priv->pm1_iobad0, data, error); +} + +gboolean +fu_superio_device_ec_write0 (FuSuperioDevice *self, guint8 data, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + if (!fu_superio_device_wait_for (self, SIO_STATUS_EC_IBF, FALSE, error)) + return FALSE; + return fu_superio_outb (priv->fd, priv->pm1_iobad0, data, error); +} + +gboolean +fu_superio_device_ec_write1 (FuSuperioDevice *self, guint8 data, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + if (!fu_superio_device_wait_for (self, SIO_STATUS_EC_IBF, FALSE, error)) + return FALSE; + return fu_superio_outb (priv->fd, priv->pm1_iobad1, data, error); +} + +static gboolean +fu_superio_device_ec_flush (FuSuperioDevice *self, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + guint8 status = 0x00; + g_autoptr(GTimer) timer = g_timer_new (); + do { + guint8 unused = 0; + if (!fu_superio_inb (priv->fd, priv->pm1_iobad1, &status, error)) + return FALSE; + if ((status & SIO_STATUS_EC_OBF) == 0) + break; + if (!fu_superio_inb (priv->fd, priv->pm1_iobad0, &unused, error)) + return FALSE; + if (g_timer_elapsed (timer, NULL) > FU_PLUGIN_SUPERIO_TIMEOUT) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "timed out whilst waiting for flush"); + return FALSE; + } + } while (TRUE); + return TRUE; +} + +gboolean +fu_superio_device_ec_get_param (FuSuperioDevice *self, guint8 param, guint8 *data, GError **error) +{ + if (!fu_superio_device_ec_write1 (self, SIO_CMD_EC_READ, error)) + return FALSE; + if (!fu_superio_device_ec_write0 (self, param, error)) + return FALSE; + return fu_superio_device_ec_read (self, 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_write1 (self, SIO_CMD_EC_WRITE, error)) + return FALSE; + if (!fu_superio_device_ec_write0 (self, param, error)) + return FALSE; + return fu_superio_device_ec_write0 (self, data, error); +} +#endif + +static gboolean +fu_superio_device_open (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + + /* open device */ + priv->fd = g_open (fu_device_get_physical_id (device), O_RDWR); + if (priv->fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open %s: %s", + fu_device_get_physical_id (device), + strerror (errno)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_device_probe (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + g_autofree gchar *devid = NULL; + g_autofree gchar *name = NULL; + + /* use the chipset name as the logical ID and for the GUID */ + fu_device_set_logical_id (device, priv->chipset); + devid = g_strdup_printf ("SuperIO-%s", priv->chipset); + fu_device_add_instance_id (device, devid); + name = g_strdup_printf ("SuperIO %s", priv->chipset); + fu_device_set_name (FU_DEVICE (self), name); + return TRUE; +} + +static gboolean +fu_superio_device_setup (FuDevice *device, GError **error) +{ + FuSuperioDeviceClass *klass = FU_SUPERIO_DEVICE_GET_CLASS (device); + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + guint8 tmp = 0x0; + + /* check port is valid */ + if (!fu_superio_inb (priv->fd, priv->pm1_iobad0, &tmp, error)) + return FALSE; + if (tmp != 0xff) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "check port!"); + return FALSE; + } + + /* check ID is correct */ + if (!fu_superio_device_check_id (self, error)) { + g_prefix_error (error, "failed to probe id: "); + return FALSE; + } + + /* dump LDNs */ + if (g_getenv ("FWUPD_SUPERIO_VERBOSE") != NULL) { + for (guint j = 0; j < SIO_LDN_LAST; j++) { + if (!fu_superio_device_regdump (self, j, error)) + return FALSE; + } + } + + /* set Power Management I/F Channel 1 LDN */ + if (!fu_superio_device_set_ldn (self, SIO_LDN_PM1, error)) + return FALSE; + + /* get the PM1 IOBAD0 address */ + if (!fu_superio_device_regval16 (self, SIO_LDNxx_IDX_IOBAD0, + &priv->pm1_iobad0, error)) + return FALSE; + + /* get the PM1 IOBAD1 address */ + if (!fu_superio_device_regval16 (self, SIO_LDNxx_IDX_IOBAD1, + &priv->pm1_iobad1, error)) + return FALSE; + + /* drain */ + if (!fu_superio_device_ec_flush (self, error)) { + g_prefix_error (error, "failed to flush: "); + 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 Registers", buf, 0x100); + } + + /* subclassed setup */ + if (klass->setup != NULL) + return klass->setup (self, error); + + /* success */ + return TRUE; +} + +static GBytes * +fu_superio_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + gsize sz = 0; + const guint8 *buf = g_bytes_get_data (fw, &sz); + const guint8 sig1[] = { 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 }; + const guint8 sig2[] = { 0x85, 0x12, 0x5a, 0x5a, 0xaa }; + + /* find signature -- maybe ignore byte 0x14 too? */ + for (gsize off = 0; off < sz; off += 16) { + if (memcmp (&buf[off], sig1, sizeof(sig1)) == 0 && + memcmp (&buf[off + 8], sig2, sizeof(sig2)) == 0) { + g_debug ("found signature at 0x%04x", (guint) off); + return g_bytes_ref (fw); + } + } + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "did not detect signature in firmware image"); + return NULL; +} + +static gboolean +fu_superio_device_close (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + if (!g_close (priv->fd, error)) + return FALSE; + priv->fd = 0; + return TRUE; +} + +static void +fu_superio_device_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (object); + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_CHIPSET: + g_value_set_string (value, priv->chipset); + break; + case PROP_PORT: + g_value_set_uint (value, priv->port); + break; + case PROP_ID: + g_value_set_uint (value, priv->id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_superio_device_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (object); + FuSuperioDevicePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_CHIPSET: + g_free (priv->chipset); + priv->chipset = g_value_dup_string (value); + break; + case PROP_PORT: + priv->port = g_value_get_uint (value); + break; + case PROP_ID: + priv->id = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_superio_device_init (FuSuperioDevice *self) +{ + fu_device_set_physical_id (FU_DEVICE (self), "/dev/port"); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_summary (FU_DEVICE (self), "Embedded Controller"); + fu_device_add_icon (FU_DEVICE (self), "computer"); +} + +static void +fu_superio_device_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_superio_device_parent_class)->finalize (object); +} + +static void +fu_superio_device_class_init (FuSuperioDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + + /* properties */ + object_class->get_property = fu_superio_device_get_property; + object_class->set_property = fu_superio_device_set_property; + pspec = g_param_spec_string ("chipset", NULL, NULL, NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_CHIPSET, pspec); + pspec = g_param_spec_uint ("port", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_PORT, pspec); + pspec = g_param_spec_uint ("id", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_ID, pspec); + + object_class->finalize = fu_superio_device_finalize; + klass_device->to_string = fu_superio_device_to_string; + klass_device->open = fu_superio_device_open; + klass_device->probe = fu_superio_device_probe; + klass_device->setup = fu_superio_device_setup; + klass_device->close = fu_superio_device_close; + klass_device->prepare_firmware = fu_superio_device_prepare_firmware; +} diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-device.h fwupd-1.2.10/plugins/superio/fu-superio-device.h --- fwupd-1.0.9/plugins/superio/fu-superio-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SUPERIO_DEVICE (fu_superio_device_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuSuperioDevice, fu_superio_device, FU, SUPERIO_DEVICE, FuDevice) + +struct _FuSuperioDeviceClass +{ + FuDeviceClass parent_class; + gboolean (*setup) (FuSuperioDevice *self, + GError **error); +}; + +gboolean fu_superio_device_ec_read (FuSuperioDevice *self, + guint8 *data, + GError **error); +gboolean fu_superio_device_ec_write0 (FuSuperioDevice *self, + guint8 data, + GError **error); +gboolean fu_superio_device_ec_write1 (FuSuperioDevice *self, + guint8 data, + GError **error); +gboolean fu_superio_device_ec_get_param (FuSuperioDevice *self, + guint8 param, + guint8 *data, + GError **error); +gboolean fu_superio_device_regval (FuSuperioDevice *self, + guint8 addr, + guint8 *data, + GError **error); +gboolean fu_superio_device_regval16 (FuSuperioDevice *self, + guint8 addr, + guint16 *data, + GError **error); +gboolean fu_superio_device_regwrite (FuSuperioDevice *self, + guint8 addr, + guint8 data, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-it85-device.c fwupd-1.2.10/plugins/superio/fu-superio-it85-device.c --- fwupd-1.0.9/plugins/superio/fu-superio-it85-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-it85-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-chunk.h" +#include "fu-superio-common.h" +#include "fu-superio-it85-device.h" + +struct _FuSuperioIt85Device { + FuSuperioDevice parent_instance; +}; + +G_DEFINE_TYPE (FuSuperioIt85Device, fu_superio_it85_device, FU_TYPE_SUPERIO_DEVICE) + +static gchar * +fu_superio_it85_device_get_str (FuSuperioDevice *self, guint8 idx, GError **error) +{ + GString *str = g_string_new (NULL); + if (!fu_superio_device_ec_write1 (self, idx, error)) + return NULL; + for (guint i = 0; i < 0xff; i++) { + guint8 c = 0; + if (!fu_superio_device_ec_read (self, &c, error)) + return NULL; + if (c == '$') + break; + g_string_append_c (str, c); + } + return g_string_free (str, FALSE); +} + +static gboolean +fu_superio_it85_device_setup (FuSuperioDevice *self, GError **error) +{ + guint8 size_tmp = 0; + g_autofree gchar *name = NULL; + g_autofree gchar *version = NULL; + + /* get EC size */ + if (!fu_superio_device_ec_get_param (self, 0xe5, &size_tmp, error)) { + g_prefix_error (error, "failed to get EC size: "); + return FALSE; + } + fu_device_set_firmware_size (FU_DEVICE (self), ((guint32) size_tmp) << 10); + + /* get EC strings */ + name = fu_superio_it85_device_get_str (self, SIO_CMD_EC_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_it85_device_get_str (self, SIO_CMD_EC_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, FWUPD_VERSION_FORMAT_UNKNOWN); + return TRUE; +} + +static void +fu_superio_it85_device_init (FuSuperioIt85Device *self) +{ +} + +static void +fu_superio_it85_device_class_init (FuSuperioIt85DeviceClass *klass) +{ + FuSuperioDeviceClass *klass_superio_device = FU_SUPERIO_DEVICE_CLASS (klass); + klass_superio_device->setup = fu_superio_it85_device_setup; +} diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-it85-device.h fwupd-1.2.10/plugins/superio/fu-superio-it85-device.h --- fwupd-1.0.9/plugins/superio/fu-superio-it85-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-it85-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-superio-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SUPERIO_IT85_DEVICE (fu_superio_it85_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuSuperioIt85Device, fu_superio_it85_device, FU, SUPERIO_IT85_DEVICE, FuSuperioDevice) + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-it89-device.c fwupd-1.2.10/plugins/superio/fu-superio-it89-device.c --- fwupd-1.0.9/plugins/superio/fu-superio-it89-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-it89-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-chunk.h" +#include "fu-superio-common.h" +#include "fu-superio-it89-device.h" + +struct _FuSuperioIt89Device { + FuSuperioDevice parent_instance; +}; + +G_DEFINE_TYPE (FuSuperioIt89Device, fu_superio_it89_device, FU_TYPE_SUPERIO_DEVICE) + +static gboolean +fu_superio_it89_device_read_ec_register (FuSuperioDevice *self, + guint16 addr, + guint8 *outval, + GError **error) +{ + if (!fu_superio_device_regwrite (self, + SIO_LDNxx_IDX_D2ADR, + SIO_DEPTH2_I2EC_ADDRH, + error)) + return FALSE; + if (!fu_superio_device_regwrite (self, + SIO_LDNxx_IDX_D2DAT, + addr >> 8, + error)) + return FALSE; + if (!fu_superio_device_regwrite (self, + SIO_LDNxx_IDX_D2ADR, + SIO_DEPTH2_I2EC_ADDRL, + error)) + return FALSE; + if (!fu_superio_device_regwrite (self, + SIO_LDNxx_IDX_D2DAT, + addr & 0xff, error)) + return FALSE; + if (!fu_superio_device_regwrite (self, + SIO_LDNxx_IDX_D2ADR, + SIO_DEPTH2_I2EC_DATA, + error)) + return FALSE; + return fu_superio_device_regval (self, + SIO_LDNxx_IDX_D2DAT, + outval, + error); +} + +static gboolean +fu_superio_it89_device_ec_size (FuSuperioDevice *self, GError **error) +{ + guint8 tmp = 0; + + /* not sure why we can't just use SIO_LDNxx_IDX_CHIPID1, + * but lets do the same as the vendor flash tool... */ + if (!fu_superio_it89_device_read_ec_register (self, GCTRL_ECHIPID1, &tmp, error)) + return FALSE; + if (tmp == 0x85) { + g_warning ("possibly IT85xx class device?!"); + fu_device_set_firmware_size (FU_DEVICE (self), 0x20000); + return TRUE; + } + g_debug ("ECHIPID1: 0x%02x", (guint) tmp); + + /* can't we just use SIO_LDNxx_IDX_CHIPVER... */ + if (!fu_superio_it89_device_read_ec_register (self, GCTRL_ECHIPVER, &tmp, error)) + return FALSE; + g_debug ("ECHIPVER: 0x%02x", (guint) tmp); + if (tmp >> 4 == 0x00) { + fu_device_set_firmware_size (FU_DEVICE (self), 0x20000); + return TRUE; + } + if (tmp >> 4 == 0x04) { + fu_device_set_firmware_size (FU_DEVICE (self), 0x30000); + return TRUE; + } + if (tmp >> 4 == 0x08) { + fu_device_set_firmware_size (FU_DEVICE (self), 0x40000); + return TRUE; + } + g_warning ("falling back to default size"); + fu_device_set_firmware_size (FU_DEVICE (self), 0x20000); + return TRUE; +} + +static gboolean +fu_superio_it89_device_setup (FuSuperioDevice *self, GError **error) +{ + guint8 version_tmp[2] = { 0x00 }; + g_autofree gchar *version = NULL; + + /* try to recover this */ + if (g_getenv ("FWUPD_SUPERIO_RECOVER") != NULL) { + fu_device_set_firmware_size (FU_DEVICE (self), 0x20000); + return TRUE; + } + + /* get version */ + 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, FWUPD_VERSION_FORMAT_PAIR); + + /* get size from the EC */ + if (!fu_superio_it89_device_ec_size (self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_it89_device_ec_pm1do_sci (FuSuperioDevice *self, guint8 val, GError **error) +{ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DOSCI, error)) + return FALSE; + if (!fu_superio_device_ec_write1 (self, val, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_superio_it89_device_ec_pm1do_smi (FuSuperioDevice *self, guint8 val, GError **error) +{ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DOCMI, error)) + return FALSE; + if (!fu_superio_device_ec_write1 (self, val, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_superio_device_ec_read_status (FuSuperioDevice *self, GError **error) +{ + guint8 tmp = 0x00; + + /* read status register */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_RDSR, error)) + return FALSE; + + /* wait for write */ + do { + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read (self, &tmp, error)) + return FALSE; + } while ((tmp & SIO_STATUS_EC_OBF) != 0); + + /* watch SCI events */ + return fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DISCI, error); +} + +static gboolean +fu_superio_device_ec_write_disable (FuSuperioDevice *self, GError **error) +{ + guint8 tmp = 0x00; + + /* read existing status */ + if (!fu_superio_device_ec_read_status (self, error)) + return FALSE; + + /* write disable */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_WRDI, error)) + return FALSE; + + /* read status register */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_RDSR, error)) + return FALSE; + + /* wait for read */ + do { + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read (self, &tmp, error)) + return FALSE; + } while ((tmp & SIO_STATUS_EC_IBF) != 0); + + /* watch SCI events */ + return fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DISCI, error); +} + +static gboolean +fu_superio_device_ec_write_enable (FuSuperioDevice *self, GError **error) +{ + guint8 tmp = 0x0; + + /* read existing status */ + if (!fu_superio_device_ec_read_status (self, error)) + return FALSE; + + /* write enable */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_WREN, error)) + return FALSE; + + /* read status register */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_RDSR, error)) + return FALSE; + + /* wait for !BUSY */ + do { + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read (self, &tmp, error)) + return FALSE; + } while ((tmp & 3) != SIO_STATUS_EC_IBF); + + /* watch SCI events */ + return fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DISCI, error); +} + +static GBytes * +fu_superio_it89_device_read_addr (FuSuperioDevice *self, + guint32 addr, + guint size, + GFileProgressCallback progress_cb, + GError **error) +{ + g_autofree guint8 *buf = NULL; + + /* check... */ + if (!fu_superio_device_ec_write_disable (self, error)) + return NULL; + if (!fu_superio_device_ec_read_status (self, error)) + return NULL; + + /* high speed read */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return NULL; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_HS_READ, error)) + return NULL; + + /* set address, MSB, MID, LSB */ + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 16, error)) + return NULL; + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 8, error)) + return NULL; + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr & 0xff, error)) + return NULL; + + /* padding for HS? */ + if (!fu_superio_it89_device_ec_pm1do_smi (self, 0x0, error)) + return NULL; + + /* read out data */ + buf = g_malloc0 (size); + for (guint i = 0; i < size; i++) { + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DI, error)) + return NULL; + if (!fu_superio_device_ec_read (self, &buf[i], error)) + return NULL; + + /* update progress */ + if (progress_cb != NULL) + progress_cb ((goffset) i, (goffset) size, self); + } + + /* check again... */ + if (!fu_superio_device_ec_read_status (self, error)) + return NULL; + + /* success */ + return g_bytes_new_take (g_steal_pointer (&buf), size); +} + +static void +fu_superio_it89_device_progress_cb (goffset current, goffset total, gpointer user_data) +{ + FuDevice *device = FU_DEVICE (user_data); + fu_device_set_progress_full (device, (gsize) current, (gsize) total); +} + +static gboolean +fu_superio_it89_device_write_addr (FuSuperioDevice *self, guint addr, GBytes *fw, GError **error) +{ + gsize size = 0; + const guint8 *buf = g_bytes_get_data (fw, &size); + + /* sanity check */ + if ((addr & 0xff) != 0x00) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "write addr unaligned, got 0x%04x", + (guint) addr); + } + if (size % 2 != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "write length not supported, got 0x%04x", + (guint) size); + } + + /* enable writes */ + if (!fu_superio_device_ec_write_enable (self, error)) + return FALSE; + + /* write DWORDs */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_WRITE_WORD, error)) + return FALSE; + + /* set address, MSB, MID, LSB */ + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 16, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 8, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr & 0xff, error)) + return FALSE; + + /* write data two bytes at a time */ + for (guint i = 0; i < size; i += 2) { + if (i > 0) { + if (!fu_superio_device_ec_read_status (self, error)) + return FALSE; + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, + SIO_SPI_CMD_WRITE_WORD, + error)) + return FALSE; + } + if (!fu_superio_it89_device_ec_pm1do_smi (self, buf[i+0], error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_smi (self, buf[i+1], error)) + return FALSE; + } + + /* reset back? */ + if (!fu_superio_device_ec_write_disable (self, error)) + return FALSE; + return fu_superio_device_ec_read_status (self, error); +} + +static gboolean +fu_superio_it89_device_erase_addr (FuSuperioDevice *self, guint addr, GError **error) +{ + /* enable writes */ + if (!fu_superio_device_ec_write_enable (self, error)) + return FALSE; + + /* sector erase */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_4K_SECTOR_ERASE, error)) + return FALSE; + + /* set address, MSB, MID, LSB */ + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 16, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 8, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_smi (self, addr & 0xff, error)) + return FALSE; + + /* watch SCI events */ + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DISCI, error)) + return FALSE; + return fu_superio_device_ec_read_status (self, error); +} + +/* The 14th byte of the 16 byte signature is always read from the hardware as + * 0x00 rather than the specified 0xAA. Fix up the firmware to match the + * .ROM file which uses 0x7F as the number of bytes to mirror to e-flash... */ +static GBytes * +fu_plugin_superio_fix_signature (FuSuperioDevice *self, GBytes *fw, GError **error) +{ + gsize sz = 0; + const guint8 *buf = g_bytes_get_data (fw, &sz); + g_autofree guint8 *buf2 = NULL; + const guint signature_offset = 0x4d; /* IT85, IT89 is 0x8d */ + + /* not big enough */ + if (sz < signature_offset + 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "image too small to fix"); + return NULL; + } + + /* not zero */ + if (buf[signature_offset] != 0x0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "nonzero signature byte"); + return NULL; + } + + /* fix signature to match SMT version */ + buf2 = g_memdup (buf, sz); + buf2[signature_offset] = 0x7f; + return g_bytes_new_take (g_steal_pointer (&buf2), sz); +} + +static GBytes * +fu_superio_it89_device_read_firmware (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + guint64 fwsize = fu_device_get_firmware_size_min (device); + g_autoptr(GBytes) blob = NULL; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ); + blob = fu_superio_it89_device_read_addr (self, 0x0, fwsize, + fu_superio_it89_device_progress_cb, + error); + return fu_plugin_superio_fix_signature (self, blob, error); +} + +static gboolean +fu_superio_it89_device_attach (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + + /* re-enable HOSTWA -- use 0xfd for LCFC */ + if (!fu_superio_device_ec_write1 (self, SIO_CMD_EC_ENABLE_HOST_WA, error)) + return FALSE; + + /* success */ + fu_device_remove_flag (self, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_superio_it89_device_detach (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + guint8 tmp = 0x00; + + /* turn off HOSTWA bit, keeping HSEMIE and HSEMW high */ + if (!fu_superio_device_ec_write1 (self, SIO_CMD_EC_DISABLE_HOST_WA, error)) + return FALSE; + if (!fu_superio_device_ec_read (self, &tmp, error)) + return FALSE; + if (tmp != 0x33) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to clear HOSTWA, got 0x%02x, expected 0x33", + tmp); + return FALSE; + } + + /* success */ + fu_device_add_flag (self, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_superio_it89_device_check_eflash (FuSuperioDevice *self, GError **error) +{ + g_autoptr(GBytes) fw = NULL; + const guint64 fwsize = fu_device_get_firmware_size_min (FU_DEVICE (self)); + const guint sigsz = 16; + + /* last 16 bytes of eeprom */ + fw = fu_superio_it89_device_read_addr (self, fwsize - sigsz, + sigsz, NULL, error); + if (fw == NULL) { + g_prefix_error (error, "failed to read signature bytes"); + return FALSE; + } + + /* cannot flash here without keyboard programmer */ + if (!fu_common_bytes_is_empty (fw)) { + gsize sz = 0; + const guint8 *buf = g_bytes_get_data (fw, &sz); + g_autoptr(GString) str = g_string_new (NULL); + for (guint i = 0; i < sz; i++) + g_string_append_printf (str, "0x%02x ", buf[i]); + if (str->len > 0) + g_string_truncate (str, str->len - 1); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "e-flash has been protected: %s", + str->str); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_it89_device_write_chunk (FuSuperioDevice *self, FuChunk *chk, GError **error) +{ + g_autoptr(GBytes) fw1 = NULL; + g_autoptr(GBytes) fw2 = NULL; + g_autoptr(GBytes) fw3 = NULL; + + /* erase page */ + if (!fu_superio_it89_device_erase_addr (self, chk->address, error)) { + g_prefix_error (error, "failed to erase @0x%04x", (guint) chk->address); + return FALSE; + } + + /* check erased */ + fw1 = fu_superio_it89_device_read_addr (self, chk->address, + chk->data_sz, NULL, + error); + if (fw1 == NULL) { + g_prefix_error (error, "failed to read erased " + "bytes @0x%04x", (guint) chk->address); + return FALSE; + } + if (!fu_common_bytes_is_empty (fw1)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "sector was not erased"); + return FALSE; + } + + /* skip empty page */ + fw2 = g_bytes_new_static (chk->data, chk->data_sz); + if (fu_common_bytes_is_empty (fw2)) + return TRUE; + + /* write page */ + if (!fu_superio_it89_device_write_addr (self, chk->address, fw2, error)) { + g_prefix_error (error, "failed to write @0x%04x", (guint) chk->address); + return FALSE; + } + + /* verify page */ + fw3 = fu_superio_it89_device_read_addr (self, chk->address, + chk->data_sz, NULL, + error); + if (fw3 == NULL) { + g_prefix_error (error, "failed to read written " + "bytes @0x%04x", (guint) chk->address); + return FALSE; + } + if (!fu_common_bytes_compare (fw2, fw3, error)) { + g_prefix_error (error, "failed to verify @0x%04x", + (guint) chk->address); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_it89_device_get_jedec_id (FuSuperioDevice *self, guint8 *id, GError **error) +{ + /* read status register */ + if (!fu_superio_device_ec_read_status (self, error)) + return FALSE; + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error)) + return FALSE; + if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_JEDEC_ID, error)) + return FALSE; + + /* wait for reads */ + for (guint i = 0; i < 4; i++) { + if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read (self, &id[i], error)) + return FALSE; + } + + /* watch SCI events */ + return fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DISCI, error); +} + +static gboolean +fu_superio_it89_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + guint8 id[4] = { 0x0 }; + g_autoptr(GBytes) fw_fixed = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* check JEDEC ID */ + if (!fu_superio_it89_device_get_jedec_id (self, id, error)) { + g_prefix_error (error, "failed to get JEDEC ID: "); + return FALSE; + } + if (id[0] != 0xff || id[1] != 0xff || id[2] != 0xfe || id[3] != 0xff) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "JEDEC ID not valid, 0x%02x%02x%02x%02x", + id[0], id[1], id[2], id[3]); + return FALSE; + } + + /* check eflash is writable */ + if (!fu_superio_it89_device_check_eflash (self, error)) + return FALSE; + + /* disable the mirroring of e-flash */ + if (g_getenv ("FWUPD_SUPERIO_DISABLE_MIRROR") != NULL) { + fw_fixed = fu_plugin_superio_fix_signature (self, fw, error); + if (fw_fixed == NULL) + return FALSE; + } else { + fw_fixed = g_bytes_ref (fw); + } + + /* chunks of 1kB, skipping the final chunk */ + chunks = fu_chunk_array_new_from_bytes (fw_fixed, 0x00, 0x00, 0x400); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len - 1; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + + /* try this many times; the failure-to-flash case leaves you + * without a keyboard and future boot may completely fail */ + for (guint j = 0;; j++) { + g_autoptr(GError) error_chk = NULL; + if (fu_superio_it89_device_write_chunk (self, chk, &error_chk)) + break; + if (j > 5) { + g_propagate_error (error, g_steal_pointer (&error_chk)); + return FALSE; + } + g_warning ("failure %u: %s", j, error_chk->message); + } + + /* set progress */ + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + + /* success */ + fu_device_set_progress (device, 100); + return TRUE; +} + +static void +fu_superio_it89_device_init (FuSuperioIt89Device *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_ONLY_OFFLINE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); +} + +static void +fu_superio_it89_device_class_init (FuSuperioIt89DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuSuperioDeviceClass *klass_superio_device = FU_SUPERIO_DEVICE_CLASS (klass); + klass_device->attach = fu_superio_it89_device_attach; + klass_device->detach = fu_superio_it89_device_detach; + klass_device->read_firmware = fu_superio_it89_device_read_firmware; + klass_device->write_firmware = fu_superio_it89_device_write_firmware; + klass_superio_device->setup = fu_superio_it89_device_setup; +} diff -Nru fwupd-1.0.9/plugins/superio/fu-superio-it89-device.h fwupd-1.2.10/plugins/superio/fu-superio-it89-device.h --- fwupd-1.0.9/plugins/superio/fu-superio-it89-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/fu-superio-it89-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-superio-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SUPERIO_IT89_DEVICE (fu_superio_it89_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuSuperioIt89Device, fu_superio_it89_device, FU, SUPERIO_IT89_DEVICE, FuSuperioDevice) + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/superio/meson.build fwupd-1.2.10/plugins/superio/meson.build --- fwupd-1.0.9/plugins/superio/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,30 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginSuperio"'] + +install_data(['superio.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_superio', + fu_hash, + sources : [ + 'fu-plugin-superio.c', + 'fu-superio-device.c', + 'fu-superio-it85-device.c', + 'fu-superio-it89-device.c', + 'fu-superio-common.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 -Nru fwupd-1.0.9/plugins/superio/README.md fwupd-1.2.10/plugins/superio/README.md --- fwupd-1.0.9/plugins/superio/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,24 @@ +SuperIO +======= + +This plugin enumerates the various ITE85* SuperIO embedded controller ICs found +in many laptops. Vendors wanting to expose the SuperIO functionality will need +to add a HwId quirk entry to `superio.quirk`. + +See https://en.wikipedia.org/wiki/Super_I/O for more details about SuperIO +and what the EC actually does. + +Other useful links: + +* https://raw.githubusercontent.com/system76/ecflash/master/ec.py +* https://github.com/system76/firmware-update/tree/master/src +* 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 -Nru fwupd-1.0.9/plugins/superio/superio.quirk fwupd-1.2.10/plugins/superio/superio.quirk --- fwupd-1.0.9/plugins/superio/superio.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/superio/superio.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,49 @@ +# N13xWU +[HwId=992f1bc7-f8ee-567a-88dd-30e5158d72ed] +SuperioChipsets=IT8587 + +# W740SU +[HwId=f00d8c4e-dce2-51c3-89d6-6cbc5fc5cdbb] +SuperioChipsets=IT8587 + +# Star LabTop Mk3 +[HwId=3dc52d2c-9e9b-5ba5-b10d-9ba1eb11dacc] +SuperioChipsets=IT8987 +InstallDuration=20 + +# Star Lite Mk2 +[HwId=d1a64840-4307-58fb-a62c-de28a07c0151] +SuperioChipsets=IT8987 +InstallDuration=20 + +[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 -Nru fwupd-1.0.9/plugins/synapticsmst/fu-plugin-synapticsmst.c fwupd-1.2.10/plugins/synapticsmst/fu-plugin-synapticsmst.c --- fwupd-1.0.9/plugins/synapticsmst/fu-plugin-synapticsmst.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/fu-plugin-synapticsmst.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Mario Limonciello * Copyright (C) 2017 Peichen Huang * Copyright (C) 2017 Richard Hughes @@ -10,20 +9,35 @@ #include "config.h" #include "synapticsmst-device.h" #include "synapticsmst-common.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" #include "fu-device-metadata.h" #define SYNAPTICS_FLASH_MODE_DELAY 3 #define SYNAPTICS_UPDATE_ENUMERATE_TRIES 3 -#define HWID_DELL_INC "85d38fda-fc0e-5c6f-808f-076984ae7978" -#define DELL_DOCK_FLASH_GUID "e7ca1f36-bf73-4574-afe6-a4ccacabf479" -#define DELL_DOCK_FUTURE_GUID "41ca7da371ef437e027272d0173bdddb3423827f" - -struct FuPluginData { - gchar *system_type; -}; +static gboolean +syanpticsmst_check_amdgpu_safe (GError **error) +{ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_auto(GStrv) lines = NULL; + + if (!g_file_get_contents ("/proc/modules", &buf, &bufsz, error)) + return FALSE; + + lines = g_strsplit (buf, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + if (g_str_has_prefix (lines[i], "amdgpu ")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "amdgpu has known issues with synapticsmst"); + return FALSE; + } + } + + return TRUE; +} static gboolean synapticsmst_common_check_supported_system (FuPlugin *plugin, GError **error) @@ -34,16 +48,10 @@ return TRUE; } - /* tests for "Dell Inc." manufacturer string - * this isn't strictly a complete tests due to OEM rebranded - * systems being excluded, but should cover most cases */ - if (!fu_plugin_check_hwid (plugin, HWID_DELL_INC)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "MST firmware updating not supported by OEM"); + /* See https://github.com/hughsie/fwupd/issues/1121 for more details */ + if (!syanpticsmst_check_amdgpu_safe (error)) return FALSE; - } + if (!g_file_test (SYSFS_DRM_DP_AUX, G_FILE_TEST_IS_DIR)) { g_set_error (error, G_IO_ERROR, @@ -54,18 +62,99 @@ return TRUE; } +/* creates MST-$str-$BOARDID */ +static void +fu_plugin_synapticsmst_create_simple_guid (FuDevice *fu_device, + SynapticsMSTDevice *device, + const gchar *str) +{ + guint16 board_id = synapticsmst_device_get_board_id (device); + g_autofree gchar *devid = g_strdup_printf ("MST-%s-%u", str, board_id); + fu_device_add_instance_id (fu_device, devid); +} + +/* creates MST-$str-$chipid-$BOARDID */ +static void +fu_plugin_synapticsmst_create_complex_guid (FuDevice *fu_device, + SynapticsMSTDevice *device, + const gchar *device_kind) +{ + const gchar *chip_id_str = synapticsmst_device_get_chip_id_str (device); + g_autofree gchar *chip_id_down = g_ascii_strdown (chip_id_str, -1); + g_autofree gchar *tmp = g_strdup_printf ("%s-%s", device_kind, chip_id_down); + + fu_plugin_synapticsmst_create_simple_guid (fu_device, device, tmp); +} + +static gboolean +fu_plugin_synapticsmst_lookup_device (FuPlugin *plugin, + FuDevice *fu_device, + SynapticsMSTDevice *device, + GError **error) +{ + const gchar *board_str; + const gchar *guid_template; + guint16 board_id = synapticsmst_device_get_board_id (device); + const gchar *chip_id_str = synapticsmst_device_get_chip_id_str (device); + g_autofree gchar *group = NULL; + g_autofree gchar *name = NULL; + + /* GUIDs used only for test mode */ + if (g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR") != NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("test-%s", chip_id_str); + fu_plugin_synapticsmst_create_simple_guid (fu_device, device, tmp); + return TRUE; + } + + /* set up the device name via quirks */ + group = g_strdup_printf ("SynapticsMSTBoardID=%u", board_id); + board_str = fu_plugin_lookup_quirk_by_id (plugin, group, + FU_QUIRKS_NAME); + if (board_str == NULL) + board_str = "Unknown Platform"; + name = g_strdup_printf ("Synaptics %s inside %s", synapticsmst_device_get_chip_id_str (device), + board_str); + fu_device_set_name (fu_device, name); + + /* build the GUIDs for the device */ + guid_template = fu_plugin_lookup_quirk_by_id (plugin, group, "DeviceKind"); + /* no quirks defined for this board */ + if (guid_template == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unknown board_id %x", + board_id); + return FALSE; + + /* this is a host system, use system ID */ + } else if (g_strcmp0 (guid_template, "system") == 0) { + const gchar *system_type = fu_plugin_get_dmi_value (plugin, + FU_HWIDS_KEY_PRODUCT_SKU); + fu_plugin_synapticsmst_create_simple_guid (fu_device, device, + system_type); + /* docks or something else */ + } else { + g_auto(GStrv) templates = NULL; + templates = g_strsplit (guid_template, ",", -1); + for (guint i = 0; templates[i] != NULL; i++) { + fu_plugin_synapticsmst_create_complex_guid (fu_device, + device, + templates[i]); + } + } + + return TRUE; +} + static gboolean fu_plugin_synaptics_add_device (FuPlugin *plugin, SynapticsMSTDevice *device, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - g_autoptr(FuDevice) dev = NULL; const gchar *kind_str = NULL; - const gchar *board_str = NULL; - GPtrArray *guids = NULL; - g_autofree gchar *name = NULL; g_autofree gchar *dev_id_str = NULL; g_autofree gchar *layer_str = NULL; g_autofree gchar *rad_str = NULL; @@ -75,73 +164,46 @@ aux_node = synapticsmst_device_get_aux_node (device); if (!synapticsmst_device_enumerate_device (device, - data->system_type, error)) { g_prefix_error (error, "Error enumerating device at %s: ", aux_node); return FALSE; } + /* create the device */ + dev = fu_device_new (); + /* Store $KIND-$AUXNODE-$LAYER-$RAD as device ID */ layer = synapticsmst_device_get_layer (device); rad = synapticsmst_device_get_rad (device); - board_str = synapticsmst_device_board_id_to_string (synapticsmst_device_get_board_id (device)); - name = g_strdup_printf ("Synaptics %s inside %s", synapticsmst_device_get_chip_id_str (device), - board_str); - guids = synapticsmst_device_get_guids (device); - if (guids->len == 0) { - g_debug ("No GUIDs found for board ID %x", - synapticsmst_device_get_board_id(device)); - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Invalid device"); - return FALSE; - } - /* Store $KIND-$AUXNODE-$LAYER-$RAD as device ID */ kind_str = synapticsmst_device_kind_to_string (synapticsmst_device_get_kind (device)); dev_id_str = g_strdup_printf ("MST-%s-%s-%u-%u", kind_str, aux_node, layer, rad); - layer_str = g_strdup_printf ("%u", layer); - rad_str = g_strdup_printf ("%u", rad); - - if (board_str == NULL) { - g_debug ("invalid board ID (%x)", synapticsmst_device_get_board_id (device)); - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Invalid device"); - return FALSE; - } - - /* create the device */ - dev = fu_device_new (); fu_device_set_id (dev, dev_id_str); + fu_device_set_physical_id (dev, aux_node); fu_device_set_metadata (dev, "SynapticsMSTKind", kind_str); fu_device_set_metadata (dev, "SynapticsMSTAuxNode", aux_node); + layer_str = g_strdup_printf ("%u", layer); fu_device_set_metadata (dev, "SynapticsMSTLayer", layer_str); + rad_str = g_strdup_printf ("%u", rad); fu_device_set_metadata (dev, "SynapticsMSTRad", rad_str); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_set_name (dev, name); fu_device_set_vendor (dev, "Synaptics"); fu_device_set_summary (dev, "Multi-Stream Transport Device"); fu_device_add_icon (dev, "computer"); - fu_device_set_version (dev, synapticsmst_device_get_version (device)); - for (guint i = 0; i < guids->len; i++) - fu_device_add_guid (dev, g_ptr_array_index (guids, i)); - - /* Set up parents */ - switch (synapticsmst_device_get_board_id (device)) { - case SYNAPTICSMST_DEVICE_BOARDID_DELL_WD15_TB16_WIRE: - fu_device_add_parent_guid (dev, DELL_DOCK_FLASH_GUID); - break; - case SYNAPTICSMST_DEVICE_BOARDID_DELL_FUTURE: - fu_device_add_parent_guid (dev, DELL_DOCK_FUTURE_GUID); - break; - default: - break; - } + fu_device_set_version (dev, synapticsmst_device_get_version (device), + FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin)); + /* create GUIDs and name */ + if (!fu_plugin_synapticsmst_lookup_device (plugin, dev, device, error)) + return FALSE; + if (!fu_device_setup (dev, error)) + return FALSE; fu_plugin_device_add (plugin, dev); fu_plugin_cache_add (plugin, dev_id_str, dev); + + /* inhibit the idle sleep of the daemon */ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE, + "SynapticsMST can cause the screen to flash when probing"); return TRUE; } @@ -257,7 +319,7 @@ dev_id_str = g_strdup_printf ("MST-DIRECT-%s-0-0", aux_node); fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str); - /* If we open succesfully a device exists here */ + /* If we open successfully a device exists here */ device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_DIRECT, aux_node, 0, 0); if (!synapticsmst_device_open (device, &error_local)) { /* No device exists here, but was there - remove from DB */ @@ -305,12 +367,13 @@ FwupdInstallFlags flags, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); g_autoptr(SynapticsMSTDevice) device = NULL; SynapticsMSTDeviceKind kind; const gchar *aux_node; guint8 layer; guint8 rad; + gboolean reboot; + gboolean install_force; /* extract details to build a new device */ kind = synapticsmst_device_kind_from_string (fu_device_get_metadata (dev, "SynapticsMSTKind")); @@ -327,33 +390,33 @@ device = synapticsmst_device_new (kind, aux_node, layer, rad); - if (!synapticsmst_device_enumerate_device (device, - data->system_type, error)) + if (!synapticsmst_device_enumerate_device (device, error)) return FALSE; - if (synapticsmst_device_board_id_to_string (synapticsmst_device_get_board_id (device)) != NULL) { - fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); - if (!synapticsmst_device_write_firmware (device, blob_fw, - fu_synapticsmst_write_progress_cb, - dev, - error)) { - g_prefix_error (error, "failed to flash firmware: "); - return FALSE; - } - } else { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Unknown device"); + reboot = !fu_device_has_custom_flag (dev, "skip-restart"); + install_force = (flags & FWUPD_INSTALL_FLAG_FORCE) != 0 || + fu_device_has_custom_flag (dev, "ignore-board-id"); + fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); + if (!synapticsmst_device_write_firmware (device, blob_fw, + fu_synapticsmst_write_progress_cb, + dev, + reboot, + install_force, + error)) { + g_prefix_error (error, "failed to flash firmware: "); return FALSE; } + if (!reboot) { + g_debug ("Skipping device restart per quirk request"); + return TRUE; + } + /* Re-run device enumeration to find the new device version */ fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART); for (guint i = 1; i <= SYNAPTICS_UPDATE_ENUMERATE_TRIES; i++) { g_autoptr(GError) error_local = NULL; g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000); if (!synapticsmst_device_enumerate_device (device, - data->system_type, &error_local)) { g_warning ("Unable to find device after %u seconds: %s", SYNAPTICS_FLASH_MODE_DELAY * i, @@ -368,21 +431,55 @@ } } } - fu_device_set_version (dev, synapticsmst_device_get_version (device)); + fu_device_set_version (dev, synapticsmst_device_get_version (device), + FWUPD_VERSION_FORMAT_TRIPLET); + + return TRUE; +} + +gboolean +fu_plugin_device_removed (FuPlugin *plugin, FuDevice *device, GError **error) +{ + const gchar *aux_node; + const gchar *kind_str; + const gchar *layer_str; + const gchar *rad_str; + g_autofree gchar *dev_id_str = NULL; + aux_node = fu_device_get_metadata (device, "SynapticsMSTAuxNode"); + if (aux_node == NULL) + return TRUE; + kind_str = fu_device_get_metadata (device, "SynapticsMSTKind"); + if (kind_str == NULL) + return TRUE; + layer_str = fu_device_get_metadata (device, "SynapticsMSTLayer"); + if (layer_str == NULL) + return TRUE; + rad_str = fu_device_get_metadata (device, "SynapticsMSTRad"); + if (rad_str == NULL) + return TRUE; + dev_id_str = g_strdup_printf ("MST-%s-%s-%s-%s", + kind_str, aux_node, layer_str, rad_str); + if (fu_plugin_cache_lookup (plugin, dev_id_str) != NULL) { + g_debug ("Removing %s from cache", dev_id_str); + fu_plugin_cache_remove (plugin, dev_id_str); + } else { + g_debug ("%s constructed but not found in cache", dev_id_str); + } return TRUE; } static gboolean fu_plugin_synapticsmst_coldplug (FuPlugin *plugin, GError **error) { + g_autoptr(GError) error_local = NULL; /* verify that this is a supported system */ if (!synapticsmst_common_check_supported_system (plugin, error)) return FALSE; /* look for host devices or already plugged in dock devices */ - if (!fu_plugin_synapticsmst_enumerate (plugin, error)) - g_debug ("error enumerating"); + if (!fu_plugin_synapticsmst_enumerate (plugin, &error_local)) + g_debug ("error enumerating: %s", error_local->message); return TRUE; } @@ -398,23 +495,12 @@ return fu_plugin_synapticsmst_coldplug (plugin, error); } -void -fu_plugin_destroy (FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - - g_free(data->system_type); -} void fu_plugin_init (FuPlugin *plugin) { - FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); - - data->system_type = - g_strdup (fu_plugin_get_dmi_value (plugin, - FU_HWIDS_KEY_PRODUCT_SKU)); - /* 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 -Nru fwupd-1.0.9/plugins/synapticsmst/fu-self-test.c fwupd-1.2.10/plugins/synapticsmst/fu-self-test.c --- fwupd-1.0.9/plugins/synapticsmst/fu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Mario Limonciello * * SPDX-License-Identifier: LGPL-2.1+ @@ -8,7 +7,6 @@ #include "config.h" #include -#include #include #include diff -Nru fwupd-1.0.9/plugins/synapticsmst/meson.build fwupd-1.2.10/plugins/synapticsmst/meson.build --- fwupd-1.0.9/plugins/synapticsmst/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,11 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsMST"'] +install_data(['synapticsmst.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_synapticsmst', + fu_hash, sources : [ 'fu-plugin-synapticsmst.c', 'synapticsmst-common.c', @@ -14,15 +19,14 @@ install : true, install_dir: plugin_dir, c_args : [ - cargs, - ], + cargs, + ], + link_with : [ + libfwupdprivate, + ], dependencies : [ plugin_deps, ], - # https://github.com/hughsie/fwupd/issues/207 - override_options : [ - 'werror=false', - ] ) if get_option('tests') @@ -31,6 +35,7 @@ cargs += '-DSOURCEDIR="' + meson.current_source_dir() + '"' e = executable( 'synapticsmst-self-test', + fu_hash, sources : [ 'fu-self-test.c', 'synapticsmst-common.c', @@ -47,16 +52,11 @@ valgrind, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : [ cargs, ], - # https://github.com/hughsie/fwupd/issues/207 - override_options : [ - 'werror=false', - ] ) test('synapticsmst-self-test', e, env: ['FWUPD_LOCALSTATEDIR=/tmp/fwupd-self-test/var']) diff -Nru fwupd-1.0.9/plugins/synapticsmst/README.md fwupd-1.2.10/plugins/synapticsmst/README.md --- fwupd-1.0.9/plugins/synapticsmst/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 @@ ## 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 -Nru fwupd-1.0.9/plugins/synapticsmst/synapticsmst-common.c fwupd-1.2.10/plugins/synapticsmst/synapticsmst-common.c --- fwupd-1.0.9/plugins/synapticsmst/synapticsmst-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/synapticsmst-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * Copyright (C) 2016 Mario Limonciello * Copyright (C) 2017 Peichen Huang @@ -9,16 +8,7 @@ #include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include #include "synapticsmst-common.h" -#include "synapticsmst-device.h" #define UNIT_SIZE 32 #define MAX_WAIT_TIME 3 /* unit : second */ @@ -30,30 +20,72 @@ guint8 rad; }; -guint8 +static gboolean synapticsmst_common_aux_node_read (SynapticsMSTConnection *connection, - gint offset, gint *buf, gint length) + guint32 offset, guint8 *buf, + gint length, GError **error) { - if (lseek (connection->fd, offset, SEEK_SET) != offset) - return DPCD_SEEK_FAIL; + if (lseek (connection->fd, offset, SEEK_SET) != offset) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to lseek"); + return FALSE; + } - if (read (connection->fd, buf, length) != length) - return DPCD_ACCESS_FAIL; + if (read (connection->fd, buf, length) != length) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to read"); + return FALSE; + } - return DPCD_SUCCESS; + return TRUE; } -static guint8 +static gboolean synapticsmst_common_aux_node_write (SynapticsMSTConnection *connection, - gint offset, const gint *buf, gint length) + guint32 offset, const guint8 *buf, + gint length, GError **error) { - if (lseek (connection->fd, offset, SEEK_SET) != offset) - return DPCD_SEEK_FAIL; + if (lseek (connection->fd, offset, SEEK_SET) != offset) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to lseek"); + return FALSE; + } + + if (write (connection->fd, buf, length) != length) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to write"); + return FALSE; + } - if (write (connection->fd, buf, length) != length) - return DPCD_ACCESS_FAIL; + return TRUE; +} + +static gboolean +synapticsmst_common_bus_read (SynapticsMSTConnection *connection, + guint32 offset, + guint8 *buf, + guint32 length, GError **error) +{ + return synapticsmst_common_aux_node_read (connection, offset, buf, + length, error); +} - return DPCD_SUCCESS; +static gboolean +synapticsmst_common_bus_write (SynapticsMSTConnection *connection, + guint32 offset, + const guint8 *buf, + guint32 length, GError **error) +{ + return synapticsmst_common_aux_node_write (connection, offset, buf, + length, error); } void @@ -73,61 +105,68 @@ return connection; } -guint8 -synapticsmst_common_read_dpcd (SynapticsMSTConnection *connection, - gint offset, gint *buf, gint length) +gboolean +synapticsmst_common_read (SynapticsMSTConnection *connection, + guint32 offset, guint8 *buf, + guint32 length, GError **error) { if (connection->layer && connection->remain_layer) { - guint8 rc, node; + guint8 node; + gboolean result; connection->remain_layer--; node = (connection->rad >> connection->remain_layer * 2) & 0x03; - rc = synapticsmst_common_rc_get_command (connection, - UPDC_READ_FROM_TX_DPCD + node, - length, offset, (guint8 *)buf); + result = synapticsmst_common_rc_get_command (connection, + UPDC_READ_FROM_TX_DPCD + node, + length, offset, (guint8 *)buf, + error); connection->remain_layer++; - return rc; + return result; } - return synapticsmst_common_aux_node_read (connection, offset, buf, length); + + return synapticsmst_common_bus_read (connection, offset, buf, length, error); } -guint8 -synapticsmst_common_write_dpcd (SynapticsMSTConnection *connection, - gint offset, - const gint *buf, - gint length) +gboolean +synapticsmst_common_write (SynapticsMSTConnection *connection, + guint32 offset, + const guint8 *buf, + guint32 length, GError **error) { if (connection->layer && connection->remain_layer) { - guint8 rc, node; + guint8 node; + gboolean result; connection->remain_layer--; node = (connection->rad >> connection->remain_layer * 2) & 0x03; - rc = synapticsmst_common_rc_set_command (connection, - UPDC_WRITE_TO_TX_DPCD + node, - length, offset, (guint8 *)buf); + result = synapticsmst_common_rc_set_command (connection, + UPDC_WRITE_TO_TX_DPCD + node, + length, offset, (guint8 *)buf, + error); connection->remain_layer++; - return rc; + return result; } - return synapticsmst_common_aux_node_write (connection, offset, buf, length); + + return synapticsmst_common_bus_write (connection, offset, buf, length, error); } -guint8 +gboolean synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, - gint rc_cmd, - gint length, - gint offset, - const guint8 *buf) -{ - guint8 rc = 0; - gint cur_offset = offset; - gint cur_length; + guint32 rc_cmd, + guint32 length, + guint32 offset, + const guint8 *buf, + GError **error) +{ + guint32 cur_offset = offset; + guint32 cur_length; gint data_left = length; gint cmd; gint readData = 0; long deadline; struct timespec t_spec; - do{ + do { if (data_left > UNIT_SIZE) { cur_length = UNIT_SIZE; } else { @@ -136,52 +175,73 @@ if (cur_length) { /* write data */ - rc = synapticsmst_common_write_dpcd (connection, REG_RC_DATA, (gint *)buf, cur_length); - if (rc) - break; + if (!synapticsmst_common_write (connection, + REG_RC_DATA, + buf, cur_length, + error)) { + g_prefix_error (error, "failure writing data register: "); + return FALSE; + } /* write offset */ - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_OFFSET, - &cur_offset, 4); - if (rc) - break; + if (!synapticsmst_common_write (connection, + REG_RC_OFFSET, + (guint8 *)&cur_offset, 4, + error)) { + g_prefix_error (error, "failure writing offset register: "); + return FALSE; + } /* write length */ - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_LEN, - &cur_length, 4); - if (rc) - break; + if (!synapticsmst_common_write (connection, + REG_RC_LEN, + (guint8 *)&cur_length, 4, + error)) { + g_prefix_error (error, "failure writing length register: "); + return FALSE; + } } /* send command */ cmd = 0x80 | rc_cmd; - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_CMD, - &cmd, 1); - if (rc) - break; + if (!synapticsmst_common_write (connection, + REG_RC_CMD, + (guint8 *)&cmd, 1, + error)) { + g_prefix_error (error, "failed to write command: "); + return FALSE; + } /* wait command complete */ clock_gettime (CLOCK_REALTIME, &t_spec); deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - rc = synapticsmst_common_read_dpcd (connection, - REG_RC_CMD, - &readData, 2); + if (!synapticsmst_common_read (connection, + REG_RC_CMD, + (guint8 *)&readData, 2, + error)) { + g_prefix_error (error, "failed to read command: "); + return FALSE; + } clock_gettime (CLOCK_REALTIME, &t_spec); if (t_spec.tv_sec > deadline) { - rc = -1; + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "timeout exceeded"); + return FALSE; } - } while (rc == 0 && readData & 0x80); + } while (readData & 0x80); + + if (readData & 0xFF00) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "remote command failed: %d", + (readData >> 8) & 0xFF); - if (rc) - break; - else if (readData & 0xFF00) { - rc = (readData >> 8) & 0xFF; - break; + return FALSE; } buf += cur_length; @@ -189,22 +249,22 @@ data_left -= cur_length; } while (data_left); - return rc; + return TRUE; } -guint8 +gboolean synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, - gint rc_cmd, - gint length, - gint offset, - guint8 *buf) -{ - guint8 rc = 0; - gint cur_offset = offset; - gint cur_length; + guint32 rc_cmd, + guint32 length, + guint32 offset, + guint8 *buf, + GError **error) +{ + guint32 cur_offset = offset; + guint32 cur_length; gint data_need = length; - gint cmd; - gint readData = 0; + guint32 cmd; + guint32 readData = 0; long deadline; struct timespec t_spec; @@ -217,56 +277,75 @@ if (cur_length) { /* write offset */ - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_OFFSET, - &cur_offset, 4); - if (rc) - break; + if (!synapticsmst_common_write (connection, + REG_RC_OFFSET, + (guint8 *)&cur_offset, 4, + error)) { + g_prefix_error (error, "failed to write offset: "); + return FALSE; + } /* write length */ - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_LEN, - &cur_length, 4); - if (rc) - break; + if (!synapticsmst_common_write (connection, + REG_RC_LEN, + (guint8 *)&cur_length, 4, + error)) { + g_prefix_error (error, "failed to write length: "); + return FALSE; + } } /* send command */ cmd = 0x80 | rc_cmd; - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_CMD, - &cmd, 1); - if (rc) - break; + if (!synapticsmst_common_write (connection, + REG_RC_CMD, + (guint8 *)&cmd, 1, + error)) { + g_prefix_error (error, "failed to write command: "); + return FALSE; + } /* wait command complete */ clock_gettime (CLOCK_REALTIME, &t_spec); deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - rc = synapticsmst_common_read_dpcd (connection, - REG_RC_CMD, - &readData, 2); + if (!synapticsmst_common_read (connection, + REG_RC_CMD, + (guint8 *)&readData, 2, + error)) { + g_prefix_error (error, "failed to read command: "); + return FALSE; + } clock_gettime (CLOCK_REALTIME, &t_spec); if (t_spec.tv_sec > deadline) { - rc = -1; + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "timeout exceeded"); + return FALSE; } - } while (rc == 0 && readData & 0x80); + } while (readData & 0x80); + + if (readData & 0xFF00) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "remote command failed: %u", + (readData >> 8) & 0xFF); - if (rc) - break; - else if (readData & 0xFF00) { - rc = (readData >> 8) & 0xFF; - break; + return FALSE; } if (cur_length) { - rc = synapticsmst_common_read_dpcd (connection, - REG_RC_DATA, - (gint *)buf, - cur_length); - if (rc) - break; + if (!synapticsmst_common_read (connection, + REG_RC_DATA, + buf, + cur_length, + error)) { + g_prefix_error (error, "failed to read data: "); + return FALSE; + } } buf += cur_length; @@ -274,125 +353,159 @@ data_need -= cur_length; } - return rc; + return TRUE; } -guint8 +gboolean synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, - gint rc_cmd, - gint cmd_length, - gint cmd_offset, + guint32 rc_cmd, + guint32 cmd_length, + guint32 cmd_offset, guint8 *cmd_data, - gint length, - guint8 *buf) + guint32 length, + guint8 *buf, + GError **error) { - guint8 rc = 0; - gint readData = 0; - gint cmd; + guint32 readData = 0; + guint32 cmd; long deadline; struct timespec t_spec; if (cmd_length) { /* write cmd data */ if (cmd_data != NULL) { - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_DATA, - (gint *)cmd_data, - cmd_length); - if (rc) - return rc; + if (!synapticsmst_common_write (connection, + REG_RC_DATA, + cmd_data, + cmd_length, + error)) { + g_prefix_error (error, "Failed to write command data: "); + return FALSE; + } } /* write offset */ - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_OFFSET, - &cmd_offset, 4); - if (rc) - return rc; + if (!synapticsmst_common_write (connection, + REG_RC_OFFSET, + (guint8 *)&cmd_offset, 4, + error)) { + g_prefix_error (error, "failed to write offset: "); + return FALSE; + } /* write length */ - rc = synapticsmst_common_write_dpcd (connection, - REG_RC_LEN, - &cmd_length, 4); - if (rc) - return rc; + if (!synapticsmst_common_write (connection, + REG_RC_LEN, + (guint8 *)&cmd_length, 4, + error)) { + g_prefix_error (error, "failed to write length: "); + return FALSE; + } } /* send command */ cmd = 0x80 | rc_cmd; - rc = synapticsmst_common_write_dpcd (connection, REG_RC_CMD, &cmd, 1); - if (rc) - return rc; + if (!synapticsmst_common_write (connection, + REG_RC_CMD, + (guint8 *)&cmd, 1, + error)) { + g_prefix_error (error, "failed to write command: "); + return FALSE; + } /* wait command complete */ clock_gettime (CLOCK_REALTIME, &t_spec); deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - rc = synapticsmst_common_read_dpcd (connection, - REG_RC_CMD, - &readData, 2); + if (!synapticsmst_common_read (connection, + REG_RC_CMD, + (guint8 *)&readData, 2, + error)) { + g_prefix_error (error, "failed to read command: "); + return FALSE; + } clock_gettime (CLOCK_REALTIME, &t_spec); - if (t_spec.tv_sec > deadline) - return -1; + if (t_spec.tv_sec > deadline) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "timeout exceeded"); + return FALSE; + + } } while (readData & 0x80); - if (rc) - return rc; - else if (readData & 0xFF00) { - rc = (readData >> 8) & 0xFF; - return rc; + if (readData & 0xFF00) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "remote command failed: %u", + (readData >> 8) & 0xFF); + + return FALSE; } if (length) { - rc = synapticsmst_common_read_dpcd (connection, - REG_RC_DATA, - (gint *)buf, length); - if (rc) - return rc; + if (!synapticsmst_common_read (connection, + REG_RC_DATA, + buf, length, + error)) { + g_prefix_error (error, "failed to read length: "); + } } - return rc; + return TRUE; } -guint8 -synapticsmst_common_enable_remote_control (SynapticsMSTConnection *connection) +gboolean +synapticsmst_common_enable_remote_control (SynapticsMSTConnection *connection, + GError **error) { const gchar *sc = "PRIUS"; - guint8 rc = 0; for (gint i = 0; i <= connection->layer; i++) { - g_autoptr(SynapticsMSTConnection) connection_tmp = NULL; - connection_tmp = synapticsmst_common_new (connection->fd, i, connection->rad); - rc = synapticsmst_common_rc_set_command (connection_tmp, + g_autoptr(SynapticsMSTConnection) connection_tmp = synapticsmst_common_new (connection->fd, i, connection->rad); + g_autoptr(GError) error_local = NULL; + if (!synapticsmst_common_rc_set_command (connection_tmp, UPDC_ENABLE_RC, - 5, 0, (guint8*)sc); - /* if we fail, try to disable and enable one more time */ - if (rc) { - g_debug ("Failed to enable remote control, retrying"); - synapticsmst_common_disable_remote_control (connection_tmp); - rc = synapticsmst_common_rc_set_command (connection_tmp, + 5, 0, (guint8*)sc, + &error_local)) { + g_debug ("Failed to enable remote control in layer %d: %s, retrying", + i, error_local->message); + + if (!synapticsmst_common_disable_remote_control (connection_tmp, error)) + return FALSE; + if (!synapticsmst_common_rc_set_command (connection_tmp, UPDC_ENABLE_RC, - 5, 0, (guint8*)sc); - if (rc) - break; + 5, 0, (guint8*)sc, + error)) { + g_prefix_error (error, + "failed to enable remote control in layer %d: ", + i); + return FALSE; + } } } - return rc; + + return TRUE; } -guint8 -synapticsmst_common_disable_remote_control (SynapticsMSTConnection *connection) +gboolean +synapticsmst_common_disable_remote_control (SynapticsMSTConnection *connection, + GError **error) { - guint8 rc = 0; - for (gint i = connection->layer; i >= 0; i--) { - g_autoptr(SynapticsMSTConnection) connection_tmp = NULL; - connection_tmp = synapticsmst_common_new (connection->fd, i, connection->rad); - rc = synapticsmst_common_rc_set_command (connection_tmp, + g_autoptr(SynapticsMSTConnection) connection_tmp = synapticsmst_common_new (connection->fd, i, connection->rad); + if (!synapticsmst_common_rc_set_command (connection_tmp, UPDC_DISABLE_RC, - 0, 0, NULL); - if (rc) - break; + 0, 0, NULL, + error)) { + g_prefix_error (error, + "failed to disable remote control in layer %d: ", + i); + return FALSE; + } } - return rc; + + return TRUE; } diff -Nru fwupd-1.0.9/plugins/synapticsmst/synapticsmst-common.h fwupd-1.2.10/plugins/synapticsmst/synapticsmst-common.h --- fwupd-1.0.9/plugins/synapticsmst/synapticsmst-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/synapticsmst-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,13 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Mario Limonciello * Copyright (C) 2017 Peichen Huang * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __SYNAPTICSMST_COMMON_H -#define __SYNAPTICSMST_COMMON_H +#pragma once #include #include @@ -15,6 +13,9 @@ #define ADDR_CUSTOMER_ID 0X10E #define ADDR_BOARD_ID 0x10F +#define ADDR_MEMORY_CUSTOMER_ID 0x170E +#define ADDR_MEMORY_BOARD_ID 0x170F + #define REG_RC_CAP 0x4B0 #define REG_RC_STATE 0X4B1 #define REG_RC_CMD 0x4B2 @@ -28,12 +29,6 @@ #define REG_FIRMWARE_VERSION 0x50A typedef enum { - DPCD_SUCCESS = 0, - DPCD_SEEK_FAIL, - DPCD_ACCESS_FAIL, -} SynapticsMstDpcdRc; - -typedef enum { UPDC_COMMAND_SUCCESS = 0, UPDC_COMMAND_INVALID, UPDC_COMMAND_UNSUPPORT, @@ -67,48 +62,48 @@ guint8 layer, guint rad); -guint8 synapticsmst_common_aux_node_read (SynapticsMSTConnection *connection, - gint offset, - gint *buf, - gint length); - -guint8 synapticsmst_common_read_dpcd (SynapticsMSTConnection *connection, - gint offset, - gint *buf, - gint length); - -guint8 synapticsmst_common_write_dpcd (SynapticsMSTConnection *connection, - gint offset, - const gint *buf, - gint length); - -guint8 synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, - gint rc_cmd, - gint length, - gint offset, - const guint8 *buf); - -guint8 synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, - gint rc_cmd, - gint length, - gint offset, - guint8 *buf); - -guint8 synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, - gint rc_cmd, - gint cmd_length, - gint cmd_offset, - guint8 *cmd_data, - gint length, - guint8 *buf); - -guint8 synapticsmst_common_enable_remote_control (SynapticsMSTConnection *connection); +gboolean synapticsmst_common_read (SynapticsMSTConnection *connection, + guint32 offset, + guint8 *buf, + guint32 length, + GError **error); + +gboolean synapticsmst_common_write (SynapticsMSTConnection *connection, + guint32 offset, + const guint8 *buf, + guint32 length, + GError **error); + +gboolean synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, + guint32 rc_cmd, + guint32 length, + guint32 offset, + const guint8 *buf, + GError **error); + + +gboolean synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, + guint32 rc_cmd, + guint32 length, + guint32 offset, + guint8 *buf, + GError **error); + +gboolean synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, + guint32 rc_cmd, + guint32 cmd_length, + guint32 cmd_offset, + guint8 *cmd_data, + guint32 length, + guint8 *buf, + GError **error); -guint8 synapticsmst_common_disable_remote_control (SynapticsMSTConnection *connection); +gboolean synapticsmst_common_enable_remote_control (SynapticsMSTConnection *connection, + GError **error); +gboolean synapticsmst_common_disable_remote_control (SynapticsMSTConnection *connection, + GError **error); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(SynapticsMSTConnection, synapticsmst_common_free) #pragma clang diagnostic pop - -#endif /* __SYNAPTICSMST_COMMON_H */ diff -Nru fwupd-1.0.9/plugins/synapticsmst/synapticsmst-device.c fwupd-1.2.10/plugins/synapticsmst/synapticsmst-device.c --- fwupd-1.0.9/plugins/synapticsmst/synapticsmst-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/synapticsmst-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * Copyright (C) 2016 Mario Limonciello * Copyright (C) 2017 Peichen Huang @@ -9,14 +8,14 @@ */ #include "config.h" -#include -#include -#include -#include -#include + +#include "fu-device-locker.h" #include "synapticsmst-device.h" #include "synapticsmst-common.h" -#include "fu-device-locker.h" + +#include +#include +#include #define BIT( n ) ( 1 << (n) ) #define FLASH_SECTOR_ERASE_4K 0x1000 @@ -38,11 +37,13 @@ #define REG_QUAD_DISABLE 0x200fc0 #define REG_HDCP22_DISABLE 0x200f90 +#define FLASH_SETTLE_TIME 5000000 /* us */ + typedef struct { SynapticsMSTDeviceKind kind; gchar *version; - SynapticsMSTDeviceBoardID board_id; + guint32 board_id; guint16 chip_id; gchar *chip_id_str; GPtrArray *guids; @@ -50,9 +51,9 @@ guint8 layer; guint16 rad; gint fd; - gboolean has_cascade; + gboolean has_cascade; gchar *fw_dir; - gboolean test_mode; + gboolean test_mode; } SynapticsMSTDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE (SynapticsMSTDevice, synapticsmst_device, G_TYPE_OBJECT) @@ -79,33 +80,6 @@ return NULL; } -const gchar * -synapticsmst_device_board_id_to_string (SynapticsMSTDeviceBoardID board_id) -{ - if (board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_X6) - return "Dell X6 Platform"; - if (board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_X7) - return "Dell X7 Platform"; - if (board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_WD15_TB16_WIRE) - return "Dell WD15/TB16 wired Dock"; - if (board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_WLD15_WIRELESS) - return "Dell WLD15 Wireless Dock"; - if (board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_X7_RUGGED) - return "Dell Rugged Platform"; - if ((board_id >> 8) == CUSTOMERID_DELL) - return "Dell Generic SynapticsMST Device"; - if ((board_id & 0x3300) || (board_id & 0x5300)) - return "SYNA EVB board"; - return "Unknown Platform"; -} - -GPtrArray * -synapticsmst_device_get_guids (SynapticsMSTDevice *device) -{ - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->guids; -} - static void synapticsmst_device_finalize (GObject *object) { @@ -119,7 +93,6 @@ g_free (priv->aux_node); g_free (priv->version); g_free (priv->chip_id_str); - g_ptr_array_unref (priv->guids); G_OBJECT_CLASS (synapticsmst_device_parent_class)->finalize (object); } @@ -136,7 +109,6 @@ priv->test_mode = TRUE; priv->fw_dir = g_strdup (tmp); } - priv->guids = g_ptr_array_new_with_free_func (g_free); } static void @@ -153,7 +125,7 @@ return priv->kind; } -SynapticsMSTDeviceBoardID +guint16 synapticsmst_device_get_board_id (SynapticsMSTDevice *device) { SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); @@ -194,15 +166,10 @@ } connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); - if (synapticsmst_common_enable_remote_control (connection)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to enable MST remote control"); + if (!synapticsmst_common_enable_remote_control (connection, error)) return FALSE; - } else { - return TRUE; - } + + return TRUE; } static gboolean @@ -239,28 +206,24 @@ } connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); - if (synapticsmst_common_disable_remote_control (connection)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to disable MST remote control"); + if (!synapticsmst_common_disable_remote_control (connection, error)) return FALSE; - } else { - return TRUE; - } + + return TRUE; } gboolean synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *device, - GError ** error, + GError **error, guint8 tx_port) { SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint8 layer = priv->layer + 1; guint16 rad = priv->rad | (tx_port << (2 * (priv->layer))); guint8 byte[4]; - guint8 rc; g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; if (priv->test_mode) return TRUE; @@ -268,28 +231,28 @@ /* reset */ priv->has_cascade = FALSE; - if (!synapticsmst_device_enable_remote_control (device, error)) { - g_prefix_error (error, - "failed to scan cascade device on tx_port %d: ", - tx_port); + /* enable remote control and disable on exit */ + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, + (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, + error); + if (locker == NULL) return FALSE; - } connection = synapticsmst_common_new (priv->fd, layer, rad); - rc = synapticsmst_common_read_dpcd (connection, REG_RC_CAP, (gint *)byte, 1); - if (rc == DPCD_SUCCESS ) { - if (byte[0] & 0x04) { - synapticsmst_common_read_dpcd (connection, REG_VENDOR_ID, (gint *)byte, 3); - if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) - priv->has_cascade = TRUE; - } + if (!synapticsmst_common_read (connection, REG_RC_CAP, byte, 1, &error_local)) { + g_debug ("No cascade device found: %s", error_local->message); + return TRUE; } - - if (!synapticsmst_device_disable_remote_control (device, error)) { - g_prefix_error (error, - "failed to scan cascade device on tx_port %d: ", - tx_port); - return FALSE; + if (byte[0] & 0x04) { + if (!synapticsmst_common_read (connection, REG_VENDOR_ID, byte, 3, error)) { + g_prefix_error (error, + "failed to read cascade device on tx_port %d: ", + tx_port); + return FALSE; + } + if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) + priv->has_cascade = TRUE; } return TRUE; @@ -302,7 +265,6 @@ GError **error) { SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - guint8 rc; if (priv->test_mode) { g_autofree gchar *filename = NULL; @@ -338,94 +300,16 @@ } close (fd); } else { - rc = synapticsmst_common_rc_get_command (connection, - UPDC_READ_FROM_EEPROM, - 2, ADDR_CUSTOMER_ID, byte); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to read from EEPROM of device"); + /* get board ID via MCU address 0x170E instead of flash access due to HDCP2.2 running */ + if (!synapticsmst_common_rc_get_command (connection, + UPDC_READ_FROM_MEMORY, + 2, + (gint)ADDR_MEMORY_CUSTOMER_ID, byte, + error)) { + g_prefix_error (error, "Memory query failed: "); return FALSE; } } - return TRUE; -} - - -/* - * Adds a GUID - * - GUID is MST-$SYSTEMID-$BOARDID - * - $BOARDID includes CUSTOMERID in first byte, BOARD in second byte - */ -static void -synapticsmst_create_guid (SynapticsMSTDevice *device, - const gchar *system) -{ - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - g_ptr_array_add (priv->guids, g_strdup_printf ("MST-%s-%u", system, priv->board_id)); -} - -static void -synapticsmst_create_dell_dock_guids (SynapticsMSTDevice *device, - const gchar *dock_type) -{ - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - const gchar *dell_docks[] = {"wd15", "tb16", "tb18", NULL}; - g_autofree gchar *chip_id_down = g_ascii_strdown (priv->chip_id_str, -1); - - for (guint i = 0; dell_docks[i] != NULL; i++) { - g_autofree gchar *tmp = NULL; - if (dock_type != NULL) { - tmp = g_strdup_printf ("%s-%s", dock_type, chip_id_down); - synapticsmst_create_guid (device, tmp); - break; - } - tmp = g_strdup_printf ("%s-%s", dell_docks[i], chip_id_down); - synapticsmst_create_guid (device, tmp); - } -} - -static gboolean -synapticsmst_create_guids (SynapticsMSTDevice *device, - const gchar *system_type, - GError **error) -{ - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - - if (priv->test_mode) { - g_autofree gchar *tmp = NULL; - tmp = g_strdup_printf ("test-%s", priv->chip_id_str); - synapticsmst_create_guid (device, tmp); - return TRUE; - } - - switch (priv->board_id >> 8) { - /* only dell is supported for today */ - case CUSTOMERID_DELL: - /* If we know the dock from another plugin, use it, otherwise make GUIDs for all those we know about */ - if (priv->board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_WD15_TB16_WIRE || - priv->board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_FUTURE) - synapticsmst_create_dell_dock_guids (device, NULL); - else if (priv->board_id == SYNAPTICSMST_DEVICE_BOARDID_DELL_WLD15_WIRELESS) - synapticsmst_create_dell_dock_guids (device, "wld15"); - /* This is a host system, use system ID */ - else - synapticsmst_create_guid (device, system_type); - break; - /* EVB development board */ - case 0: - synapticsmst_create_guid (device, "evb"); - break; - /* unknown */ - default: - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Unknown board_id %x", - priv->board_id); - return FALSE; - } return TRUE; } @@ -438,19 +322,15 @@ g_autoptr(SynapticsMSTConnection) connection = NULL; SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint32 dwData[16]; - guint8 rc; /* get used bank */ connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); - rc = synapticsmst_common_rc_get_command (connection, + if (!synapticsmst_common_rc_get_command (connection, UPDC_READ_FROM_MEMORY, ((sizeof(dwData)/sizeof(dwData[0]))*4), - (gint) 0x20010c, (guint8*) dwData); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Get active bank failed"); + (gint) 0x20010c, (guint8*) dwData, + error)) { + g_prefix_error (error, "get active bank failed: "); return FALSE; } if ((dwData[0] & BIT(7)) || (dwData[0] & BIT(30))) @@ -465,76 +345,57 @@ gboolean synapticsmst_device_enumerate_device (SynapticsMSTDevice *device, - const gchar *system_type, GError **error) { SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint8 byte[16]; - guint8 rc; guint8 bank; g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; - //FIXME? if (!synapticsmst_device_open (device, error)) { g_prefix_error (error, "Failed to open device in DP Aux Node %s: ", synapticsmst_device_get_aux_node (device)); return FALSE; } - /* enable remote control */ - if (!synapticsmst_device_enable_remote_control (device, error)) + /* enable remote control and disable on exit */ + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, + (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, + error); + if (locker == NULL) return FALSE; /* read firmware version */ connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); - rc = synapticsmst_common_read_dpcd (connection, - REG_FIRMWARE_VERSION, - (gint *)byte, 3); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to read dpcd from device"); - goto error_disable_remote; - } + if (!synapticsmst_common_read (connection, REG_FIRMWARE_VERSION, + byte, 3, error)) + return FALSE; + priv->version = g_strdup_printf ("%1d.%02d.%03d", byte[0], byte[1], byte[2]); /* read board ID */ if (!synapticsmst_device_read_board_id (device, connection, byte, error)) - goto error_disable_remote; + return FALSE; priv->board_id = (byte[0] << 8) | (byte[1]); g_debug ("BoardID %x", priv->board_id); /* read board chip_id */ - rc = synapticsmst_common_read_dpcd (connection, - REG_CHIP_ID, - (gint *)byte, 2); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to read dpcd from device"); - goto error_disable_remote; + if (!synapticsmst_common_read (connection, REG_CHIP_ID, + byte, 2, error)) { + g_prefix_error (error, "failed to read chip id: "); + return FALSE; } priv->chip_id = (byte[0] << 8) | (byte[1]); priv->chip_id_str = g_strdup_printf ("VMM%02x%02x", byte[0], byte[1]); - if (!synapticsmst_create_guids (device, system_type, error)) - goto error_disable_remote; /* if running on panamera, check the active bank (for debugging logs) */ if (priv->chip_id > 0x5000 && !synapticsmst_device_get_active_bank_panamera (device, &bank, error)) - goto error_disable_remote; - - /* disable remote control */ - if (!synapticsmst_device_disable_remote_control (device, error)) return FALSE; return TRUE; - -error_disable_remote: - synapticsmst_device_disable_remote_control (device, NULL); - return FALSE; } const gchar * @@ -589,22 +450,20 @@ static gboolean synapticsmst_device_get_flash_checksum (SynapticsMSTDevice *device, - gint length, gint offset, + guint32 length, guint32 offset, guint32 *checksum, GError **error) { SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); g_autoptr(SynapticsMSTConnection) connection = NULL; connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); - if (synapticsmst_common_rc_special_get_command (connection, + if (!synapticsmst_common_rc_special_get_command (connection, UPDC_CAL_EEPROM_CHECKSUM, length, offset, NULL, 4, - (guint8 *)checksum)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to get flash checksum"); + (guint8 *)checksum, + error)) { + g_prefix_error (error, "failed to get flash checksum: "); return FALSE; } @@ -612,7 +471,7 @@ } static guint16 -synapticsmst_device_get_crc (guint16 crc, guint8 type, guint32 length, guint8 *payload_data) +synapticsmst_device_get_crc (guint16 crc, guint8 type, guint32 length, const guint8 *payload_data) { static const guint16 CRC16_table[] = { 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, @@ -652,7 +511,7 @@ }; guint8 val; guint16 remainder = (guint16) crc; - guint8 *message = payload_data; + const guint8 *message = payload_data; if (type == CRC_8) { for (guint32 byte = 0; byte < length; ++byte) { @@ -683,13 +542,12 @@ /* Need to add Wp control ? */ us_data = rc_cmd + offset; - if (synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_FLASH_ERASE, - 2, 0, (guint8 *)&us_data)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "can't sector erase flash at offset %x", offset); + 2, 0, (guint8 *)&us_data, + error)) { + g_prefix_error (error, "can't sector erase flash at offset %x", + offset); return FALSE; } @@ -698,7 +556,7 @@ static gboolean synapticsmst_device_update_esm (SynapticsMSTDevice *device, - guint8 *payload_data, + const guint8 *payload_data, GFileProgressCallback progress_cb, gpointer progress_data, GError **error) @@ -729,39 +587,37 @@ } g_debug ("ESM checksum %x doesn't match expected %x", flash_checksum, checksum); - /* erase ESM firmware */ - for (guint32 i = 0; i < 4; i++) { - if (!synapticsmst_device_set_flash_sector_erase (device, FLASH_SECTOR_ERASE_64K, i + 4, error)) { - g_prefix_error (error, "Failed to erase sector %u: ", i); - return FALSE; - } - } - /* update ESM firmware */ - write_loops = (esm_sz / unit_sz); - if (esm_sz % unit_sz) - write_loops++; + write_loops = esm_sz / unit_sz; for (guint retries_cnt = 0; ; retries_cnt++) { guint32 write_idx = 0; guint32 write_offset = EEPROM_ESM_OFFSET; - guint8 *esm_code_ptr = &payload_data[EEPROM_ESM_OFFSET]; + const guint8 *esm_code_ptr = &payload_data[EEPROM_ESM_OFFSET]; + + /* erase ESM firmware; erase failure is fatal */ + for (guint32 j = 0; j < 4; j++) { + if (!synapticsmst_device_set_flash_sector_erase (device, + FLASH_SECTOR_ERASE_64K, + j + 4, + error)) { + g_prefix_error (error, "failed to erase sector %u: ", j); + return FALSE; + } + } + + g_debug ("Waiting for flash clear to settle"); + g_usleep (FLASH_SETTLE_TIME); + + /* write firmware */ for (guint32 i = 0; i < write_loops; i++) { - guint8 rc; - rc = synapticsmst_common_rc_set_command (connection, + g_autoptr(GError) error_local = NULL; + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, unit_sz, write_offset, - esm_code_ptr + write_idx); - if (rc) { - /* repeat once */ - rc = synapticsmst_common_rc_set_command (connection, - UPDC_WRITE_TO_EEPROM, - unit_sz, - write_offset, - esm_code_ptr + write_idx); - } - if (rc) { - g_debug ("attempt %u: ESM update failed", retries_cnt); + esm_code_ptr + write_idx, + &error_local)) { + g_warning ("failed to write ESM: %s", error_local->message); break; } write_offset += unit_sz; @@ -796,7 +652,7 @@ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, - "ESM update failed after %u tries", retries_cnt); + "checksum did not match after %u tries", retries_cnt); return FALSE; } } @@ -808,7 +664,7 @@ static gboolean synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *device, guint32 payload_len, - guint8 *payload_data, + const guint8 *payload_data, GFileProgressCallback progress_cb, gpointer progress_data, GError **error) @@ -818,7 +674,6 @@ guint32 data_to_write = 0; guint32 offset = 0; guint32 write_loops = 0; - guint8 rc = 0; write_loops = (payload_len / BLOCK_UNIT); data_to_write = payload_len; @@ -834,27 +689,32 @@ if (!synapticsmst_device_set_flash_sector_erase (device, 0xffff, 0, error)) return FALSE; g_debug ("Waiting for flash clear to settle"); - g_usleep (5000000); + g_usleep (FLASH_SETTLE_TIME); for (guint32 i = 0; i < write_loops; i++) { + g_autoptr(GError) error_local = NULL; guint8 length = BLOCK_UNIT; if (data_to_write < BLOCK_UNIT) length = data_to_write; - rc = synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, length, offset, - payload_data + offset); - if (rc) { + payload_data + offset, + &error_local)) { + g_warning ("Failed to write flash offset 0x%04x: %s, retrying", + offset, error_local->message); /* repeat once */ - rc = synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, length, offset, - payload_data + offset); + payload_data + offset, + error)) { + g_prefix_error (error, "can't write flash offset 0x%04x: ", + offset); + return FALSE; + } } - if (rc) - break; - offset += length; data_to_write -= length; if (progress_cb != NULL) { @@ -863,14 +723,6 @@ progress_data); } } - if (rc) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "can't write flash at offset 0x%04x", - offset); - return FALSE; - } /* check data just written */ for (guint32 i = 0; i < payload_len; i++) @@ -880,13 +732,8 @@ payload_len, 0, &flash_checksum, - error)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to read checksum"); + error)) return FALSE; - } if (checksum == flash_checksum) break; g_debug ("attempt %u: checksum %x didn't match %x", retries_cnt, flash_checksum, checksum); @@ -908,7 +755,7 @@ static gboolean synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *device, guint32 payload_len, - guint8 *payload_data, + const guint8 *payload_data, GFileProgressCallback progress_cb, gpointer progress_data, GError **error) @@ -920,7 +767,6 @@ guint32 write_loops = 0; guint8 bank_in_use; guint8 bank_to_update = BANKTAG_1; - guint8 rc; guint8 readBuf[256]; guint8 tagData[16]; struct tm *pTM; @@ -967,32 +813,31 @@ FLASH_SECTOR_ERASE_64K, erase_offset, error)) return FALSE; g_debug ("Waiting for flash clear to settle"); - g_usleep (5000000); + g_usleep (FLASH_SETTLE_TIME); /* write */ write_idx = 0; write_offset = EEPROM_BANK_OFFSET * bank_to_update; connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); for (guint32 i = 0; i < write_loops ; i++ ) { - rc = synapticsmst_common_rc_set_command (connection, + g_autoptr(GError) error_local = NULL; + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, unit_sz, write_offset, - payload_data + write_idx); - if (rc) { + payload_data + write_idx, + &error_local)) { + g_warning ("Write failed: %s, retrying", error_local->message); /* repeat once */ - rc = synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, unit_sz, write_offset, - payload_data + write_idx); - } - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "FW write fail"); - return FALSE; + payload_data + write_idx, + error)) { + g_prefix_error (error, "firmware write failed: "); + return FALSE; + } } write_offset += unit_sz; @@ -1008,14 +853,12 @@ checksum = synapticsmst_device_get_crc ( 0, 16, fw_size, payload_data ); for (guint32 i = 0; i < 4; i++) { g_usleep (1000); /* wait crc calculation */ - if (synapticsmst_common_rc_special_get_command (connection, + if (!synapticsmst_common_rc_special_get_command (connection, UPDC_CAL_EEPROM_CHECK_CRC16, fw_size, (EEPROM_BANK_OFFSET * bank_to_update), - NULL, 4, (guint8 *)(&flash_checksum))) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to get flash checksum"); + NULL, 4, (guint8 *)(&flash_checksum), + error)) { + g_prefix_error (error, "Failed to get flash checksum: "); return FALSE; } } @@ -1049,29 +892,23 @@ for (guint32 retries_cnt = 0; ; retries_cnt++) { gboolean match = TRUE; - rc = synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, 16, (EEPROM_BANK_OFFSET * bank_to_update + EEPROM_TAG_OFFSET), - tagData); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to write TAG"); + tagData, + error)) { + g_prefix_error (error, "failed to write tag: "); return FALSE; } g_usleep (200); - rc = synapticsmst_common_rc_get_command (connection, + if (!synapticsmst_common_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 16, (EEPROM_BANK_OFFSET * bank_to_update + EEPROM_TAG_OFFSET), - readBuf); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to read Tag"); + readBuf, + error)) { + g_prefix_error (error, "failed to read tag: "); return FALSE; } for (guint32 i = 0; i < 16; i++){ @@ -1092,15 +929,12 @@ } /* set tag invalid*/ - rc = synapticsmst_common_rc_get_command (connection, + if (!synapticsmst_common_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 1, (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), - tagData); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to read Tag from flash"); + tagData, + error)) { + g_prefix_error (error, "failed to read tag from flash: "); return FALSE; } @@ -1118,21 +952,21 @@ /* CRC8 is 0xff, set it to 0x00 */ } else { tagData[1] = 0x00; - rc = synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, 1, (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), - &tagData[1]); + &tagData[1], + error)) { + g_prefix_error (error, "failed to clear CRC: "); + return FALSE; + } } - - rc = synapticsmst_common_rc_get_command (connection, + if (!synapticsmst_common_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 1, (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), - readBuf); - if (rc) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Failed to read CRC from flash"); + readBuf, + error)) { + g_prefix_error (error, "failed to read CRC from flash: "); return FALSE; } if ((readBuf[0] == 0xff && tagData[0] != 0xff) || @@ -1157,11 +991,7 @@ SynapticsMSTChipKind chip_type, GError **error) { - guint8 *payload_data; gsize payload_len, payload_len_max; - gint checksum = 0; - guint32 offset = 0; - guint32 code_size = 0; switch (chip_type) { case SYNAPTICSMST_CHIP_KIND_PANAMERA: @@ -1180,8 +1010,8 @@ } - /* get firmware data and check size */ - payload_data = g_bytes_get_data (fw, &payload_len); + /* check size */ + payload_len = g_bytes_get_size (fw); if (payload_len > payload_len_max || payload_len == 0) { g_set_error (error, G_IO_ERROR, @@ -1192,117 +1022,6 @@ return FALSE; } - /* check firmware content */ - for (guint8 i = 0; i < 128; i++) - checksum += *(payload_data + i); - if (checksum & 0xff) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "EDID checksum error: %d", - checksum); - return FALSE; - } - /* EDID */ - checksum = 0; - offset = 128; - for (guint8 i = 0; i < 128; i++) - checksum += *(payload_data + offset + i); - - if (checksum & 0xff) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "EDID checksum error"); - return FALSE; - } - /* CFG 0 */ - checksum = 0; - offset = 0x100; - for (guint16 i = 0; i < 256; i++) - checksum += *(payload_data + offset + i); - - if (checksum & 0xff) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "configuration checksum error"); - return FALSE; - } - /* CFG 1 */ - checksum = 0; - offset = 0x200; - for (guint16 i = 0; i < 256; i++) - checksum += *(payload_data + offset + i); - if (checksum & 0xff) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "configuration checksum error"); - return FALSE; - } - /* Firmware Size */ - checksum = 0; - offset = 0x400; - if (chip_type == SYNAPTICSMST_CHIP_KIND_PANAMERA) { - code_size = (*(payload_data + offset) << 24); - code_size += (*(payload_data + offset + 1) << 16); - code_size += (*(payload_data + offset + 2) << 8); - code_size += (*(payload_data + offset + 3)); - if (code_size >= 0x02ffff) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "invalid firmware size"); - return FALSE; - } - } else { - code_size = (*(payload_data + offset) << 8) + *(payload_data + offset + 1); - if (code_size >= 0xffff) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "invalid firmware size"); - return FALSE; - } - } - /* Firmware Checksum */ - if (chip_type == SYNAPTICSMST_CHIP_KIND_PANAMERA) { - guint32 i = 0; - for ( i = 0; i < code_size + 1; i++ ) { - checksum += *(payload_data + offset + 0x10 + i); - } - if ((checksum & 0xffff)!=((*(payload_data + 0x408) << 8) | (*(payload_data + 0x409)))) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "firmware checksum error"); - return FALSE; - } - checksum = 0; - offset = EEPROM_ESM_OFFSET; - for (i = 0; i < (payload_len - EEPROM_ESM_OFFSET); i++) { - checksum += *(payload_data + offset + i); - } - if ((checksum & 0xffff)!=((*(payload_data + 0x40A) << 8) | (*(payload_data + 0x40B)))) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "ESM firmware checksum error"); - return FALSE; - } - } else { - for (guint32 i = 0; i < (code_size + 17); i++) - checksum += *(payload_data + offset + i); - - if (checksum & 0xff) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "firmware checksum error"); - return FALSE; - } - } return TRUE; } @@ -1320,13 +1039,11 @@ /* disable ESM first */ dwData[0] = 0x21; - if (synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, - 4, (gint)REG_ESM_DISABLE, (guint8*)dwData)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "ESM disable failed"); + 4, (gint)REG_ESM_DISABLE, (guint8*)dwData, + error)) { + g_prefix_error (error, "ESM disable failed: "); return FALSE; } @@ -1334,47 +1051,39 @@ g_usleep (200); /* disable QUAD mode */ - if (synapticsmst_common_rc_get_command (connection, + if (!synapticsmst_common_rc_get_command (connection, UPDC_READ_FROM_MEMORY, ((sizeof(dwData)/sizeof(dwData[0]))*4), - (gint)REG_QUAD_DISABLE, (guint8*)dwData)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Quad query failed"); + (gint)REG_QUAD_DISABLE, (guint8*)dwData, + error)) { + g_prefix_error (error, "quad query failed: "); return FALSE; } dwData[0] = 0x00; - if (synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, - 4, (gint)REG_QUAD_DISABLE, (guint8*)dwData)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Quad disable failed"); + 4, (gint)REG_QUAD_DISABLE, (guint8*)dwData, + error)) { + g_prefix_error (error, "quad disable failed: "); return FALSE; } /* disable HDCP2.2 */ - if (synapticsmst_common_rc_get_command (connection, + if (!synapticsmst_common_rc_get_command (connection, UPDC_READ_FROM_MEMORY, - 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "HDCP query failed"); + 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData, + error)) { + g_prefix_error (error, "HDCP query failed: "); return FALSE; } dwData[0] = dwData[0] & (~BIT(2)); - if (synapticsmst_common_rc_set_command (connection, + if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, - 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "HDCP disable failed"); + 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData, + error)) { + g_prefix_error (error, "HDCP disable failed: "); return FALSE; } @@ -1388,12 +1097,15 @@ g_autoptr(SynapticsMSTConnection) connection = NULL; SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint8 dwData[4] = {0xF5, 0, 0 ,0}; + g_autoptr(GError) error_local = NULL; /* issue the reboot command, ignore return code (triggers before returning) */ connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); - synapticsmst_common_rc_set_command (connection, - UPDC_WRITE_TO_MEMORY, - 4, (gint) 0x2000FC, (guint8*) &dwData); + if (!synapticsmst_common_rc_set_command (connection, + UPDC_WRITE_TO_MEMORY, + 4, (gint) 0x2000FC, (guint8*) &dwData, + &error_local)) + g_debug ("failed to restart: %s", error_local->message); return TRUE; } @@ -1403,6 +1115,8 @@ GBytes *fw, GFileProgressCallback progress_cb, gpointer progress_data, + gboolean reboot, + gboolean install_force, GError **error) { const guint8 *payload_data; @@ -1423,21 +1137,16 @@ return FALSE; } - /* TODO: May need a way to override this to cover field - * issues of invalid firmware flashed*/ /* check firmware and board ID again */ tmp = (*(payload_data + ADDR_CUSTOMER_ID) << 8) + *(payload_data + ADDR_BOARD_ID); - if (synapticsmst_device_get_board_id (device) >> 8 == 0) { - g_warning ("EVB board detected, bypassing customer ID check"); - } else { - if (tmp != synapticsmst_device_get_board_id (device)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "board ID mismatch"); - return FALSE; - } + if (tmp != synapticsmst_device_get_board_id (device) && !install_force) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "board ID mismatch"); + return FALSE; } + /* open device */ if (!synapticsmst_device_open (device, error)) { g_prefix_error (error, @@ -1447,10 +1156,17 @@ } /* enable remote control and disable on exit */ - locker = fu_device_locker_new_full (device, - (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, - (FuDeviceLockerFunc) synapticsmst_device_restart, - error); + if (reboot) { + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, + (FuDeviceLockerFunc) synapticsmst_device_restart, + error); + } else { + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, + (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, + error); + } if (locker == NULL) return FALSE; @@ -1545,14 +1261,18 @@ } connection = synapticsmst_common_new (priv->fd, 0, 0); - if (synapticsmst_common_aux_node_read (connection, REG_RC_CAP, (gint *)byte, 1) == DPCD_SUCCESS) { - if (byte[0] & 0x04) { - synapticsmst_common_aux_node_read (connection, - REG_VENDOR_ID, - (gint *)byte, 3); - if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) - return TRUE; + if (!synapticsmst_common_read (connection, REG_RC_CAP, byte, 1, error)) { + g_prefix_error (error, "failed to read device: "); + return FALSE; + } + if (byte[0] & 0x04) { + if (!synapticsmst_common_read (connection, REG_VENDOR_ID, + byte, 3, error)) { + g_prefix_error (error, "failed to read vendor ID: "); + return FALSE; } + if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) + return TRUE; } /* not a correct device */ @@ -1560,7 +1280,5 @@ G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "no device"); - close (priv->fd); - priv->fd = 0; return FALSE; } diff -Nru fwupd-1.0.9/plugins/synapticsmst/synapticsmst-device.h fwupd-1.2.10/plugins/synapticsmst/synapticsmst-device.h --- fwupd-1.0.9/plugins/synapticsmst/synapticsmst-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/synapticsmst-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * Copyright (C) 2016 Mario Limonciello * Copyright (C) 2017 Peichen Huang @@ -7,10 +6,8 @@ * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __SYNAPTICSMST_DEVICE_H -#define __SYNAPTICSMST_DEVICE_H +#pragma once -#include #include G_BEGIN_DECLS @@ -42,17 +39,6 @@ } SynapticsMSTDeviceKind; typedef enum { - SYNAPTICSMST_DEVICE_BOARDID_EVB = 0x00, - SYNAPTICSMST_DEVICE_BOARDID_DELL_FUTURE = 0x103, - SYNAPTICSMST_DEVICE_BOARDID_DELL_X6 = 0x110, - SYNAPTICSMST_DEVICE_BOARDID_DELL_X7, - SYNAPTICSMST_DEVICE_BOARDID_DELL_WD15_TB16_WIRE, - SYNAPTICSMST_DEVICE_BOARDID_DELL_WLD15_WIRELESS, - SYNAPTICSMST_DEVICE_BOARDID_DELL_X7_RUGGED = 0X115, - SYNAPTICSMST_DEVICE_BOARDID_UNKNOWN = 0xFF, -} SynapticsMSTDeviceBoardID; - -typedef enum { SYNAPTICSMST_CHIP_KIND_UNKNOWN, SYNAPTICSMST_CHIP_KIND_TESLA_LEAF, SYNAPTICSMST_CHIP_KIND_PANAMERA, @@ -70,7 +56,6 @@ /* helpers */ SynapticsMSTDeviceKind synapticsmst_device_kind_from_string (const gchar *kind); const gchar *synapticsmst_device_kind_to_string (SynapticsMSTDeviceKind kind); -const gchar *synapticsmst_device_board_id_to_string (SynapticsMSTDeviceBoardID board_id); GPtrArray *synapticsmst_device_get_guids (SynapticsMSTDevice *device); gboolean synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *device, GError **error, @@ -80,25 +65,23 @@ /* getters */ SynapticsMSTDeviceKind synapticsmst_device_get_kind (SynapticsMSTDevice *device); -SynapticsMSTDeviceBoardID synapticsmst_device_get_board_id (SynapticsMSTDevice *device); +guint16 synapticsmst_device_get_board_id (SynapticsMSTDevice *device); const gchar *synapticsmst_device_get_version (SynapticsMSTDevice *device); const gchar *synapticsmst_device_get_chip_id_str (SynapticsMSTDevice *device); const gchar *synapticsmst_device_get_aux_node (SynapticsMSTDevice *device); guint16 synapticsmst_device_get_rad (SynapticsMSTDevice *device); guint8 synapticsmst_device_get_layer (SynapticsMSTDevice *device); -gboolean -synapticsmst_device_get_cascade (SynapticsMSTDevice *device); +gboolean synapticsmst_device_get_cascade (SynapticsMSTDevice *device); /* object methods */ gboolean synapticsmst_device_enumerate_device (SynapticsMSTDevice *devices, - const gchar *sytem_type, GError **error); gboolean synapticsmst_device_write_firmware (SynapticsMSTDevice *device, GBytes *fw, GFileProgressCallback progress_cb, gpointer user_data, + gboolean reboot, + gboolean install_force, GError **error); G_END_DECLS - -#endif /* __SYNAPTICSMST_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/synapticsmst/synapticsmst_evb.quirk fwupd-1.2.10/plugins/synapticsmst/synapticsmst_evb.quirk --- fwupd-1.0.9/plugins/synapticsmst/synapticsmst_evb.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/synapticsmst_evb.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +# Synaptics MST early validation board support +# +# This is not installed by default, but can be used to exercise new boards +# that don't yet have customer ID or board ID bytes filled out. +# +# To use it, load the quirk file into the quirks directory for the fwupd installation +# Usually this is /usr/share/fwupd/quirks.d +# +# Note: The flag "ignore-board-id" will be used to ignore the board ID checking in +# during flashing. This shouldn't be used in practice for production boards. +# + +[SynapticsMSTBoardID=2] +Name = Synaptics EVB development board +DeviceKind = panamera_evb + +[Guid=MST-panamera_evb-vmm5331-2] +Flags = ignore-board-id diff -Nru fwupd-1.0.9/plugins/synapticsmst/synapticsmst.quirk fwupd-1.2.10/plugins/synapticsmst/synapticsmst.quirk --- fwupd-1.0.9/plugins/synapticsmst/synapticsmst.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synapticsmst/synapticsmst.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,40 @@ +# GUID generation for Synaptics MST plugin +# +# SynapticsMSTBoardID is the 16 bit board ID which contains: +# * Customer ID in first byte +# * Board ID in the second byte +# +# DeviceKind = system +# * Will map to a GUID containing HwID product SKU +# * These GUIDs will look like MST-${PRODUCTSKU}-${BOARDID} +# DeviceKind != system +# * Will map to a GUID containing each comma delimitted substring +# * These GUIDs will look like MST-${DEVICEKIND}-${CHIPID}-${BOARDID} +# +# By default the Synaptics MST device will restart after update +# To override this behavior add the custom flag "skip-restart" +# + +[SynapticsMSTBoardID=272] +Name = Dell X6 Platform +DeviceKind = system + +[SynapticsMSTBoardID=273] +Name = Dell X7 Platform +DeviceKind = system + +[SynapticsMSTBoardID=274] +Name = Dell WD15/TB16/TB18 wired Dock +DeviceKind = wd15,tb16,tb18 + +[SynapticsMSTBoardID=275] +Name = Dell WLD15 Wireless Dock +DeviceKind = wld15 + +[SynapticsMSTBoardID=277] +Name = Dell Rugged Platform +DeviceKind = system + +[SynapticsMSTBoardID=259] +Name = Dell dock +DeviceKind = panamera diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/data/lsusb.txt fwupd-1.2.10/plugins/synaptics-prometheus/data/lsusb.txt --- fwupd-1.0.9/plugins/synaptics-prometheus/data/lsusb.txt 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/data/lsusb.txt 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,69 @@ +Bus 001 Device 043: ID 06cb:00a9 Synaptics, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 16 + bDeviceProtocol 255 + bMaxPacketSize0 8 + idVendor 0x06cb Synaptics, Inc. + idProduct 0x00a9 + bcdDevice 0.00 + iManufacturer 0 + iProduct 0 + iSerial 1 942cfe315551 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0027 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 4 +Device Status: 0x0000 + (Bus Powered) Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/synaptics-prometheus/data/test.pkg and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/synaptics-prometheus/data/test.pkg differ diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-dump.c fwupd-1.2.10/plugins/synaptics-prometheus/fu-dump.c --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-dump.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-dump.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaprom-firmware.h" + +static gboolean +fu_dump_parse (const gchar *filename, GError **error) +{ + gchar *data = NULL; + gsize len = 0; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) array = NULL; + if (!g_file_get_contents (filename, &data, &len, error)) + return FALSE; + blob = g_bytes_new_take (data, len); + array = fu_synaprom_firmware_new (blob, error); + return array != NULL; +} + +static gboolean +fu_dump_generate (const gchar *filename, GError **error) +{ + const gchar *data; + gsize len = 0; + g_autoptr(GBytes) blob = NULL; + blob = fu_synaprom_firmware_generate (); + data = g_bytes_get_data (blob, &len); + return g_file_set_contents (filename, data, len, error); +} + +int +main (int argc, char **argv) +{ + g_autoptr(GError) error = NULL; + if (argc == 2) { + if (!fu_dump_parse (argv[1], &error)) { + g_printerr ("parse failed: %s\n", error->message); + return 1; + } + } else if (argc == 3 && g_strcmp0 (argv[2], "gen") == 0) { + if (!fu_dump_generate (argv[1], &error)) { + g_printerr ("generate failed: %s\n", error->message); + return 1; + } + } else { + g_printerr ("firmware filename required\n"); + return 2; + } + g_print ("OK!\n"); + return 0; +} diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-plugin-synaptics-prometheus.c fwupd-1.2.10/plugins/synaptics-prometheus/fu-plugin-synaptics-prometheus.c --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-plugin-synaptics-prometheus.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-plugin-synaptics-prometheus.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaprom-device.h" + +#include "fu-plugin-vfuncs.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.synaptics.prometheus"); +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuSynapromDevice) dev = NULL; + + /* open the device */ + dev = fu_synaprom_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + + /* success */ + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} + +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_update (FuPlugin *plugin, + FuDevice *dev, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* write the firmware */ + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + if (!fu_device_write_firmware (dev, blob_fw, flags, error)) + return FALSE; + + /* success */ + return TRUE; +} + +gboolean +fu_plugin_update_reload (FuPlugin *plugin, FuDevice *dev, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* get the new version number */ + locker = fu_device_locker_new (dev, error); + if (locker == NULL) { + g_prefix_error (error, "failed to re-open device: "); + return FALSE; + } + + /* success */ + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-self-test.c fwupd-1.2.10/plugins/synaptics-prometheus/fu-self-test.c --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-plugin-private.h" +#include "fu-test.h" + +#include "fu-synaprom-device.h" +#include "fu-synaprom-firmware.h" + +static void +fu_test_synaprom_firmware_func (void) +{ + const guint8 *buf; + gsize sz = 0; + g_autofree gchar *filename = NULL; + g_autoptr(FuSynapromDevice) device = fu_synaprom_device_new (NULL); + g_autoptr(GBytes) blob1 = NULL; + g_autoptr(GBytes) blob2 = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) firmware = NULL; + + filename = fu_test_get_filename (TESTDATADIR, "test.pkg"); + g_assert_nonnull (filename); + fw = fu_common_get_contents_bytes (filename, &error); + g_assert_no_error (error); + g_assert_nonnull (fw); + buf = g_bytes_get_data (fw, &sz); + g_assert_cmpint (sz, ==, 294); + g_assert_cmpint (buf[0], ==, 0x01); + g_assert_cmpint (buf[1], ==, 0x00); + firmware = fu_synaprom_firmware_new (fw, &error); + g_assert_no_error (error); + g_assert_nonnull (firmware); + + /* does not exist */ + blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, 0, NULL); + g_assert_null (blob1); + blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, + FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER, + NULL); + g_assert_null (blob1); + + /* header needs to exist */ + blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, + FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER, + &error); + g_assert_no_error (error); + g_assert_nonnull (blob1); + buf = g_bytes_get_data (blob1, &sz); + g_assert_cmpint (sz, ==, 24); + g_assert_cmpint (buf[0], ==, 0x41); + g_assert_cmpint (buf[1], ==, 0x00); + g_assert_cmpint (buf[2], ==, 0x00); + g_assert_cmpint (buf[3], ==, 0x00); + g_assert_cmpint (buf[4], ==, 0xff); + + /* payload needs to exist */ + fu_synaprom_device_set_version (device, 10, 1, 1234); + blob2 = fu_synaprom_device_prepare_fw (FU_DEVICE (device), fw, + FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert_nonnull (blob2); + buf = g_bytes_get_data (blob2, &sz); + g_assert_cmpint (sz, ==, 2); + g_assert_cmpint (buf[0], ==, 'R'); + g_assert_cmpint (buf[1], ==, 'H'); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + g_test_add_func ("/synaprom/firmware", fu_test_synaprom_firmware_func); + return g_test_run (); +} diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-common.c fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-common.c --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-synaprom-common.h" + +enum { + FU_SYNAPROM_RESULT_OK = 0, + FU_SYNAPROM_RESULT_GEN_OPERATION_CANCELED = 103, + FU_SYNAPROM_RESULT_GEN_INVALID = 110, + FU_SYNAPROM_RESULT_GEN_BAD_PARAM = 111, + FU_SYNAPROM_RESULT_GEN_NULL_POINTER = 112, + FU_SYNAPROM_RESULT_GEN_UNEXPECTED_FORMAT = 114, + FU_SYNAPROM_RESULT_GEN_TIMEOUT = 117, + FU_SYNAPROM_RESULT_GEN_OBJECT_DOESNT_EXIST = 118, + FU_SYNAPROM_RESULT_GEN_ERROR = 119, + FU_SYNAPROM_RESULT_SENSOR_MALFUNCTIONED = 202, + FU_SYNAPROM_RESULT_SYS_OUT_OF_MEMORY = 602, +}; + +GByteArray * +fu_synaprom_request_new (guint8 cmd, const gpointer data, gsize len) +{ + GByteArray *blob = g_byte_array_new (); + g_byte_array_append (blob, &cmd, 1); + if (data != NULL) + g_byte_array_append (blob, data, len); + return blob; +} + +GByteArray * +fu_synaprom_reply_new (gsize cmdlen) +{ + GByteArray *blob = g_byte_array_new (); + g_byte_array_set_size (blob, cmdlen); + return blob; +} + +gboolean +fu_synaprom_error_from_status (guint16 status, GError **error) +{ + if (status == FU_SYNAPROM_RESULT_OK) + return TRUE; + switch (status) { + case FU_SYNAPROM_RESULT_GEN_OPERATION_CANCELED: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CANCELLED, + "cancelled"); + break; + case FU_SYNAPROM_RESULT_GEN_BAD_PARAM: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "bad parameter"); + break; + case FU_SYNAPROM_RESULT_GEN_NULL_POINTER: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "NULL pointer"); + break; + case FU_SYNAPROM_RESULT_GEN_UNEXPECTED_FORMAT: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unexpected format"); + break; + case FU_SYNAPROM_RESULT_GEN_TIMEOUT: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "timed out"); + break; + case FU_SYNAPROM_RESULT_GEN_OBJECT_DOESNT_EXIST: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "object does not exist"); + break; + case FU_SYNAPROM_RESULT_GEN_ERROR: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "generic error"); + break; + case FU_SYNAPROM_RESULT_SENSOR_MALFUNCTIONED: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_INITIALIZED, + "sensor malfunctioned"); + break; + case FU_SYNAPROM_RESULT_SYS_OUT_OF_MEMORY: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_AGAIN, + "out of heap memory"); + break; + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "error status: 0x%x", + status); + } + return FALSE; +} diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-common.h fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-common.h --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +GByteArray *fu_synaprom_request_new (guint8 cmd, + const gpointer data, + gsize len); +GByteArray *fu_synaprom_reply_new (gsize cmdlen); +gboolean fu_synaprom_error_from_status (guint16 status, + GError **error); diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-config.c fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-config.c --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-config.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-config.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaprom-common.h" +#include "fu-synaprom-config.h" +#include "fu-synaprom-firmware.h" + +struct _FuSynapromConfig { + FuDevice parent_instance; + FuSynapromDevice *device; + guint32 configid1; /* config ID1 */ + guint32 configid2; /* config ID2 */ +}; + +/* Iotas can exceed the size of available RAM in the part. + * In order to allow the host to read them the IOTA_FIND command supports + * transferring iotas with multiple commands */ +typedef struct __attribute__((packed)) { + guint16 itype; /* type of iotas to find */ + guint16 flags; /* flags, see below */ + guint8 maxniotas; /* maximum number of iotas to return, 0 = unlimited */ + guint8 firstidx; /* first index of iotas to return */ + guint8 dummy[2]; + guint32 offset; /* byte offset of data to return */ + guint32 nbytes; /* maximum number of bytes to return */ +} FuSynapromCmdIotaFind; + +/* this is followed by a chain of iotas, as follows */ +typedef struct __attribute__((packed)) { + guint16 status; + guint32 fullsize; + guint16 nbytes; + guint16 itype; +} FuSynapromReplyIotaFindHdr; + +/* this iota contains the configuration id and version */ +typedef struct __attribute__((packed)) { + guint32 config_id1; /* YYMMDD */ + guint32 config_id2; /* HHMMSS */ + guint16 version; + guint16 unused[3]; +} FuSynapromIotaConfigVersion; + +#define FU_SYNAPROM_CMD_IOTA_FIND_FLAGS_ALLIOTAS 0x0001 /* itype ignored*/ +#define FU_SYNAPROM_CMD_IOTA_FIND_FLAGS_READMAX 0x0002 /* nbytes ignored */ +#define FU_SYNAPROM_MAX_IOTA_READ_SIZE (64 * 1024) /* max size of iota data returned */ + +#define FU_SYNAPROM_IOTA_ITYPE_CONFIG_VERSION 0x0009 /* Configuration id and version */ + +G_DEFINE_TYPE (FuSynapromConfig, fu_synaprom_config, FU_TYPE_DEVICE) + +enum { + PROP_0, + PROP_DEVICE, + PROP_LAST +}; + +static gboolean +fu_synaprom_config_setup (FuDevice *device, GError **error) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + FuSynapromCmdIotaFind cmd = { 0x0 }; + FuSynapromIotaConfigVersion cfg; + FuSynapromReplyIotaFindHdr hdr; + g_autofree gchar *version = NULL; + g_autoptr(GByteArray) reply = NULL; + g_autoptr(GByteArray) request = NULL; + + /* get IOTA */ + cmd.itype = GUINT16_TO_LE((guint16)FU_SYNAPROM_IOTA_ITYPE_CONFIG_VERSION); + cmd.flags = GUINT16_TO_LE((guint16)FU_SYNAPROM_CMD_IOTA_FIND_FLAGS_READMAX); + request = fu_synaprom_request_new (FU_SYNAPROM_CMD_IOTA_FIND, &cmd, sizeof(cmd)); + reply = fu_synaprom_reply_new (sizeof(FuSynapromReplyIotaFindHdr) + FU_SYNAPROM_MAX_IOTA_READ_SIZE); + if (!fu_synaprom_device_cmd_send (self->device, request, reply, 5000, error)) + return FALSE; + if (reply->len < sizeof(hdr) + sizeof(cfg)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "CFG return data invalid size: 0x%04x", + reply->len); + return FALSE; + } + memcpy (&hdr, reply->data, sizeof(hdr)); + if (GUINT32_FROM_LE(hdr.itype) != FU_SYNAPROM_IOTA_ITYPE_CONFIG_VERSION) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "CFG iota had invalid itype: 0x%04x", + GUINT32_FROM_LE(hdr.itype)); + return FALSE; + } + memcpy (&cfg, reply->data + sizeof(hdr), sizeof(cfg)); + self->configid1 = GUINT32_FROM_LE(cfg.config_id1); + self->configid2 = GUINT32_FROM_LE(cfg.config_id2); + g_debug ("id1=%u, id2=%u, ver=%u", + self->configid1, self->configid2, + GUINT16_FROM_LE(cfg.version)); + + /* no downgrades are allowed */ + version = g_strdup_printf ("%04u", GUINT16_FROM_LE(cfg.version)); + fu_device_set_version (FU_DEVICE (self), version, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version_lowest (FU_DEVICE (self), version); + return TRUE; +} + +static GBytes * +fu_synaprom_config_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + FuSynapromFirmwareCfgHeader hdr; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) firmware = NULL; + guint32 product; + guint32 id1; + + /* parse the firmware */ + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + firmware = fu_synaprom_firmware_new (fw, error); + if (firmware == NULL) + return NULL; + + /* check the update header product and version */ + blob = fu_synaprom_firmware_get_bytes_by_tag (firmware, + FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER, + error); + if (blob == NULL) + return NULL; + if (g_bytes_get_size (blob) != sizeof(hdr)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "CFG metadata is invalid"); + return NULL; + } + memcpy (&hdr, g_bytes_get_data (blob, NULL), sizeof(hdr)); + product = GUINT32_FROM_LE(hdr.product); + if (product != FU_SYNAPROM_PRODUCT_PROMETHEUS) { + if (flags & FWUPD_INSTALL_FLAG_FORCE) { + g_warning ("CFG metadata not compatible, " + "got 0x%02x expected 0x%02x", + product, (guint) FU_SYNAPROM_PRODUCT_PROMETHEUS); + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "CFG metadata not compatible, " + "got 0x%02x expected 0x%02x", + product, (guint) FU_SYNAPROM_PRODUCT_PROMETHEUS); + return NULL; + } + } + id1 = GUINT32_FROM_LE(hdr.id1); + if (id1 != self->configid1) { + if (flags & FWUPD_INSTALL_FLAG_FORCE) { + g_warning ("CFG version not compatible, " + "got %u expected %u", + id1, self->configid1); + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "CFG version not compatible, " + "got %u expected %u", + id1, self->configid1); + return NULL; + } + } + + /* get payload */ + return fu_synaprom_firmware_get_bytes_by_tag (firmware, + FU_SYNAPROM_FIRMWARE_TAG_CFG_PAYLOAD, + error); +} + +static gboolean +fu_synaprom_config_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + /* I assume the CFG/MFW difference is detected in the device...*/ + return fu_synaprom_device_write_fw (self->device, fw, error); +} + +static void +fu_synaprom_config_init (FuSynapromConfig *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_logical_id (FU_DEVICE (self), "cfg"); + fu_device_set_name (FU_DEVICE (self), "Prometheus IOTA Config"); +} + +static void +fu_synaprom_config_finalize (GObject *obj) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (obj); + g_object_unref (self->device); +} + +static void +fu_synaprom_config_constructed (GObject *obj) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (obj); + g_autofree gchar *devid = NULL; + + /* append the firmware kind to the generated GUID */ + devid = g_strdup_printf ("USB\\VID_%04X&PID_%04X-cfg", + fu_usb_device_get_vid (FU_USB_DEVICE (self->device)), + fu_usb_device_get_pid (FU_USB_DEVICE (self->device))); + fu_device_add_instance_id (FU_DEVICE (self), devid); + + G_OBJECT_CLASS (fu_synaprom_config_parent_class)->constructed (obj); +} + +static void +fu_synaprom_config_get_property (GObject *obj, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (obj); + switch (prop_id) { + case PROP_DEVICE: + g_value_set_object (value, self->device); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +fu_synaprom_config_set_property (GObject *obj, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (obj); + switch (prop_id) { + case PROP_DEVICE: + g_set_object (&self->device, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static gboolean +fu_synaprom_config_open (FuDevice *device, GError **error) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + return fu_device_open (FU_DEVICE (self->device), error); +} + +static gboolean +fu_synaprom_config_close (FuDevice *device, GError **error) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + return fu_device_close (FU_DEVICE (self->device), error); +} + +static gboolean +fu_synaprom_config_attach (FuDevice *device, GError **error) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + return fu_device_attach (FU_DEVICE (self->device), error); +} + +static gboolean +fu_synaprom_config_detach (FuDevice *device, GError **error) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + return fu_device_detach (FU_DEVICE (self->device), error); +} + +static void +fu_synaprom_config_class_init (FuSynapromConfigClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->constructed = fu_synaprom_config_constructed; + object_class->finalize = fu_synaprom_config_finalize; + object_class->get_property = fu_synaprom_config_get_property; + object_class->set_property = fu_synaprom_config_set_property; + klass_device->write_firmware = fu_synaprom_config_write_firmware; + klass_device->prepare_firmware = fu_synaprom_config_prepare_firmware; + klass_device->open = fu_synaprom_config_open; + klass_device->close = fu_synaprom_config_close; + klass_device->setup = fu_synaprom_config_setup; + klass_device->attach = fu_synaprom_config_attach; + klass_device->detach = fu_synaprom_config_detach; + + pspec = g_param_spec_object ("device", NULL, NULL, + FU_TYPE_SYNAPROM_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_DEVICE, pspec); +} + +FuSynapromConfig * +fu_synaprom_config_new (FuSynapromDevice *device) +{ + FuSynapromConfig *self; + self = g_object_new (FU_TYPE_SYNAPROM_CONFIG, + "device", device, + NULL); + return FU_SYNAPROM_CONFIG (self); +} diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-config.h fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-config.h --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-config.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-config.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" +#include "fu-synaprom-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SYNAPROM_CONFIG (fu_synaprom_config_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapromConfig, fu_synaprom_config, FU, SYNAPROM_CONFIG, FuDevice) + +FuSynapromConfig *fu_synaprom_config_new (FuSynapromDevice *device); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-device.c fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-device.c --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaprom-common.h" +#include "fu-synaprom-config.h" +#include "fu-synaprom-device.h" +#include "fu-synaprom-firmware.h" + +struct _FuSynapromDevice { + FuUsbDevice parent_instance; + guint8 vmajor; + guint8 vminor; +}; + +/* vendor-specific USB control requets to write DFT word (Hayes) */ +#define FU_SYNAPROM_USB_CTRLREQUEST_VENDOR_WRITEDFT 21 + +/* endpoint addresses for command and fingerprint data */ +#define FU_SYNAPROM_USB_REQUEST_EP 0x01 +#define FU_SYNAPROM_USB_REPLY_EP 0x81 +#define FU_SYNAPROM_USB_FINGERPRINT_EP 0x82 +#define FU_SYNAPROM_USB_INTERRUPT_EP 0x83 + +/* le */ +typedef struct __attribute__((packed)) { + guint16 status; +} FuSynapromReplyGeneric; + +/* le */ +typedef struct __attribute__((packed)) { + guint16 status; + guint32 buildtime; /* Unix-style build time */ + guint32 buildnum; /* build number */ + guint8 vmajor; /* major version */ + guint8 vminor; /* minor version */ + guint8 target; /* target, e.g. VCSFW_TARGET_ROM */ + guint8 product; /* product, e.g. VCSFW_PRODUCT_FALCON */ + guint8 siliconrev; /* silicon revision */ + guint8 formalrel; /* boolean: non-zero -> formal release */ + guint8 platform; /* Platform (PCB) revision */ + guint8 patch; /* patch level */ + guint8 serial_number[6]; /* 48-bit Serial Number */ + guint8 security[2]; /* bytes 0 and 1 of OTP */ + guint32 patchsig; /* opaque patch signature */ + guint8 iface; /* interface type, see below */ + guint8 otpsig[3]; /* OTP Patch Signature */ + guint16 otpspare1; /* spare space */ + guint8 reserved; /* reserved byte */ + guint8 device_type; /* device type */ +} FuSynapromReplyGetVersion; + +/* the following bits describe security options in +** FuSynapromReplyGetVersion::security[1] bit-field */ +#define FU_SYNAPROM_SECURITY1_PROD_SENSOR (1 << 5) + +G_DEFINE_TYPE (FuSynapromDevice, fu_synaprom_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_synaprom_device_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + if (!g_usb_device_claim_interface (usb_device, 0x0, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + return FALSE; + } + return TRUE; +} + +gboolean +fu_synaprom_device_cmd_send (FuSynapromDevice *device, + GByteArray *request, + GByteArray *reply, + guint timeout_ms, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); + gboolean ret; + gsize actual_len = 0; + + if (g_getenv ("FWUPD_SYNAPROM_VERBOSE") != NULL) { + fu_common_dump_full (G_LOG_DOMAIN, "REQST", + request->data, request->len, 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + ret = g_usb_device_bulk_transfer (usb_device, + FU_SYNAPROM_USB_REQUEST_EP, + request->data, + request->len, + &actual_len, + timeout_ms, NULL, error); + if (!ret) { + g_prefix_error (error, "failed to request: "); + return FALSE; + } + if (actual_len < request->len) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only sent 0x%04x of 0x%04x", + (guint) actual_len, request->len); + return FALSE; + } + + ret = g_usb_device_bulk_transfer (usb_device, + FU_SYNAPROM_USB_REPLY_EP, + reply->data, + reply->len, + NULL, /* allowed to return short read */ + timeout_ms, NULL, error); + if (!ret) { + g_prefix_error (error, "failed to reply: "); + return FALSE; + } + if (g_getenv ("FWUPD_SYNAPROM_VERBOSE") != NULL) { + fu_common_dump_full (G_LOG_DOMAIN, "REPLY", + reply->data, actual_len, 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + + /* parse as FuSynapromReplyGeneric */ + if (reply->len >= sizeof(FuSynapromReplyGeneric)) { + FuSynapromReplyGeneric *hdr = (FuSynapromReplyGeneric *) reply->data; + return fu_synaprom_error_from_status (GUINT16_FROM_LE(hdr->status), error); + } + + /* success */ + return TRUE; +} + +void +fu_synaprom_device_set_version (FuSynapromDevice *self, + guint8 vmajor, guint8 vminor, guint32 buildnum) +{ + g_autofree gchar *str = NULL; + + /* set display version */ + str = g_strdup_printf ("%02u.%02u.%u", vmajor, vminor, buildnum); + fu_device_set_version (FU_DEVICE (self), str, FWUPD_VERSION_FORMAT_TRIPLET); + + /* we need this for checking the firmware compatibility later */ + self->vmajor = vmajor; + self->vminor = vminor; +} + +static void +fu_synaprom_device_set_serial_number (FuSynapromDevice *self, guint64 serial_number) +{ + g_autofree gchar *str = NULL; + str = g_strdup_printf ("%" G_GUINT64_FORMAT, serial_number); + fu_device_set_serial (FU_DEVICE (self), str); +} + +static gboolean +fu_synaprom_device_setup (FuDevice *device, GError **error) +{ + FuSynapromDevice *self = FU_SYNAPROM_DEVICE (device); + FuSynapromReplyGetVersion pkt; + guint32 product; + guint64 serial_number = 0; + g_autoptr(GByteArray) request = NULL; + g_autoptr(GByteArray) reply = NULL; + + /* get version */ + request = fu_synaprom_request_new (FU_SYNAPROM_CMD_GET_VERSION, NULL, 0); + reply = fu_synaprom_reply_new (sizeof(FuSynapromReplyGetVersion)); + if (!fu_synaprom_device_cmd_send (self, request, reply, 250, error)) { + g_prefix_error (error, "failed to get version: "); + return FALSE; + } + memcpy (&pkt, reply->data, sizeof(pkt)); + product = GUINT32_FROM_LE(pkt.product); + g_debug ("product ID is %u, version=%u.%u, buildnum=%u prod=%i", + product, pkt.vmajor, pkt.vminor, GUINT32_FROM_LE(pkt.buildnum), + pkt.security[1] & FU_SYNAPROM_SECURITY1_PROD_SENSOR); + fu_synaprom_device_set_version (self, pkt.vmajor, pkt.vminor, + GUINT32_FROM_LE(pkt.buildnum)); + + /* get serial number */ + memcpy (&serial_number, pkt.serial_number, sizeof(pkt.serial_number)); + fu_synaprom_device_set_serial_number (self, serial_number); + + /* check device type */ + if (product == FU_SYNAPROM_PRODUCT_PROMETHEUS) { + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } else if (product == FU_SYNAPROM_PRODUCT_PROMETHEUSPBL || + product == FU_SYNAPROM_PRODUCT_PROMETHEUSMSBL) { + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "device %u is not supported by this plugin", + product); + return FALSE; + } + + /* add updatable config child, if this is a production sensor */ + if (pkt.security[1] & FU_SYNAPROM_SECURITY1_PROD_SENSOR) { + g_autoptr(FuSynapromConfig) cfg = fu_synaprom_config_new (self); + if (!fu_device_setup (FU_DEVICE (cfg), error)) { + g_prefix_error (error, "failed to get config version: "); + return FALSE; + } + fu_device_add_child (FU_DEVICE (device), FU_DEVICE (cfg)); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaprom_device_cmd_download_chunk (FuSynapromDevice *device, + const GByteArray *chunk, + GError **error) +{ + g_autoptr(GByteArray) request = NULL; + g_autoptr(GByteArray) reply = NULL; + request = fu_synaprom_request_new (FU_SYNAPROM_CMD_BOOTLDR_PATCH, + chunk->data, chunk->len); + reply = fu_synaprom_reply_new (sizeof(FuSynapromReplyGeneric)); + return fu_synaprom_device_cmd_send (device, request, reply, 20000, error); +} + +GBytes * +fu_synaprom_device_prepare_fw (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapromDevice *self = FU_SYNAPROM_DEVICE (device); + FuSynapromFirmwareMfwHeader hdr; + guint32 product; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) firmware = NULL; + + /* parse the firmware */ + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + firmware = fu_synaprom_firmware_new (fw, error); + if (firmware == NULL) + return NULL; + + /* check the update header product and version */ + blob = fu_synaprom_firmware_get_bytes_by_tag (firmware, + FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER, + error); + if (blob == NULL) + return NULL; + if (g_bytes_get_size (blob) != sizeof(hdr)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "MFW metadata is invalid"); + return NULL; + } + memcpy (&hdr, g_bytes_get_data (blob, NULL), sizeof(hdr)); + product = GUINT32_FROM_LE(hdr.product); + if (product != FU_SYNAPROM_PRODUCT_PROMETHEUS) { + if (flags & FWUPD_INSTALL_FLAG_FORCE) { + g_warning ("MFW metadata not compatible, " + "got 0x%02x expected 0x%02x", + product, (guint) FU_SYNAPROM_PRODUCT_PROMETHEUS); + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "MFW metadata not compatible, " + "got 0x%02x expected 0x%02x", + product, (guint) FU_SYNAPROM_PRODUCT_PROMETHEUS); + return NULL; + } + } + if (hdr.vmajor != self->vmajor || hdr.vminor != self->vminor) { + if (flags & FWUPD_INSTALL_FLAG_FORCE) { + g_warning ("MFW version not compatible, " + "got %u.%u expected %u.%u", + hdr.vmajor, hdr.vminor, + self->vmajor, self->vminor); + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "MFW version not compatible, " + "got %u.%u expected %u.%u", + hdr.vmajor, hdr.vminor, + self->vmajor, self->vminor); + return NULL; + } + } + + /* get payload */ + return fu_synaprom_firmware_get_bytes_by_tag (firmware, + FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD, + error); +} + +gboolean +fu_synaprom_device_write_fw (FuSynapromDevice *self, GBytes *fw, GError **error) +{ + const guint8 *buf; + gsize sz = 0; + + /* write chunks */ + fu_device_set_progress (FU_DEVICE (self), 10); + fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE); + buf = g_bytes_get_data (fw, &sz); + while (sz != 0) { + guint32 chunksz; + g_autoptr(GByteArray) chunk = g_byte_array_new (); + + /* get chunk size */ + if (sz < sizeof(guint32)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "No enough data for patch len"); + return FALSE; + } + memcpy (&chunksz, buf, sizeof(guint32)); + buf += sizeof(guint32); + sz -= sizeof(guint32); + if (sz < chunksz) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "No enough data for patch chunk"); + return FALSE; + } + + /* download chunk */ + g_byte_array_append (chunk, buf, chunksz); + if (!fu_synaprom_device_cmd_download_chunk (self, chunk, error)) + return FALSE; + + /* next chunk */ + buf += chunksz; + sz -= chunksz; + } + + /* success! */ + fu_device_set_progress (FU_DEVICE (self), 100); + return TRUE; +} + +static gboolean +fu_synaprom_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapromDevice *self = FU_SYNAPROM_DEVICE (device); + return fu_synaprom_device_write_fw (self, fw, error); +} + +static gboolean +fu_synaprom_device_attach (FuDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); + gboolean ret; + gsize actual_len = 0; + guint8 data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + ret = g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + FU_SYNAPROM_USB_CTRLREQUEST_VENDOR_WRITEDFT, + 0x0000, 0x0000, + data, sizeof(data), &actual_len, + 2000, NULL, error); + if (!ret) + return FALSE; + if (actual_len != sizeof(data)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only sent 0x%04x of 0x%04x", + (guint) actual_len, (guint) sizeof(data)); + return FALSE; + } + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!g_usb_device_reset (usb_device, error)) { + g_prefix_error (error, "failed to force-reset device: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaprom_device_detach (FuDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); + gboolean ret; + gsize actual_len = 0; + guint8 data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; + + ret = g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + FU_SYNAPROM_USB_CTRLREQUEST_VENDOR_WRITEDFT, + 0x0000, 0x0000, + data, sizeof(data), &actual_len, + 2000, NULL, error); + if (!ret) + return FALSE; + if (actual_len != sizeof(data)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only sent 0x%04x of 0x%04x", + (guint) actual_len, (guint) sizeof(data)); + return FALSE; + } + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!g_usb_device_reset (usb_device, error)) { + g_prefix_error (error, "failed to force-reset device: "); + return FALSE; + } + return TRUE; +} + +static void +fu_synaprom_device_init (FuSynapromDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_name (FU_DEVICE (self), "Prometheus"); + fu_device_set_summary (FU_DEVICE (self), "Fingerprint reader"); + fu_device_set_vendor (FU_DEVICE (self), "Synaptics"); + fu_device_add_icon (FU_DEVICE (self), "touchpad-disabled"); +} + +static void +fu_synaprom_device_class_init (FuSynapromDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->write_firmware = fu_synaprom_device_write_firmware; + klass_device->prepare_firmware = fu_synaprom_device_prepare_fw; + klass_device->setup = fu_synaprom_device_setup; + klass_device->attach = fu_synaprom_device_attach; + klass_device->detach = fu_synaprom_device_detach; + klass_usb_device->open = fu_synaprom_device_open; +} + +FuSynapromDevice * +fu_synaprom_device_new (FuUsbDevice *device) +{ + FuSynapromDevice *self; + self = g_object_new (FU_TYPE_SYNAPROM_DEVICE, NULL); + if (device != NULL) + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return FU_SYNAPROM_DEVICE (self); +} diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-device.h fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-device.h --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SYNAPROM_DEVICE (fu_synaprom_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapromDevice, fu_synaprom_device, FU, SYNAPROM_DEVICE, FuUsbDevice) + +#define FU_SYNAPROM_PRODUCT_PROMETHEUS 65 /* Prometheus (b1422) */ +#define FU_SYNAPROM_PRODUCT_PROMETHEUSPBL 66 +#define FU_SYNAPROM_PRODUCT_PROMETHEUSMSBL 67 + +#define FU_SYNAPROM_CMD_GET_VERSION 0x01 +#define FU_SYNAPROM_CMD_BOOTLDR_PATCH 0x7d +#define FU_SYNAPROM_CMD_IOTA_FIND 0x8e + +FuSynapromDevice *fu_synaprom_device_new (FuUsbDevice *device); +gboolean fu_synaprom_device_cmd_send (FuSynapromDevice *device, + GByteArray *request, + GByteArray *reply, + guint timeout_ms, + GError **error); +gboolean fu_synaprom_device_write_fw (FuSynapromDevice *self, + GBytes *fw, + GError **error); + +/* for self tests */ +void fu_synaprom_device_set_version (FuSynapromDevice *self, + guint8 vmajor, + guint8 vminor, + guint32 buildnum); +GBytes *fu_synaprom_device_prepare_fw (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-firmware.c fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-firmware.c --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-firmware.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaprom-firmware.h" + +typedef struct __attribute__((packed)) { + guint16 tag; + guint32 bufsz; +} FuSynapromFirmwareHdr; + +typedef struct { + guint16 tag; + GBytes *bytes; +} FuSynapromFirmwareItem; + +/* use only first 12 bit of 16 bits as tag value */ +#define FU_SYNAPROM_FIRMWARE_TAG_MAX 0xfff0 +#define FU_SYNAPROM_FIRMWARE_SIGSIZE 0x0100 + +static void +fu_synaprom_firmware_item_free (FuSynapromFirmwareItem *item) +{ + g_bytes_unref (item->bytes); + g_free (item); +} + +static const gchar * +fu_synaprom_firmware_tag_to_string (guint16 tag) +{ + if (tag == FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER) + return "mfw-update-header"; + if (tag == FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD) + return "mfw-update-payload"; + if (tag == FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER) + return "cfg-update-header"; + if (tag == FU_SYNAPROM_FIRMWARE_TAG_CFG_PAYLOAD) + return "cfg-update-payload"; + return NULL; +} + +GPtrArray * +fu_synaprom_firmware_new (GBytes *blob, GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + gsize offset = 0; + g_autoptr(GPtrArray) firmware = NULL; + + g_return_val_if_fail (blob != NULL, NULL); + + firmware = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_synaprom_firmware_item_free); + buf = g_bytes_get_data (blob, &bufsz); + + /* 256 byte signature as footer */ + if (bufsz < FU_SYNAPROM_FIRMWARE_SIGSIZE + sizeof(FuSynapromFirmwareHdr)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "blob is too small to be firmware"); + return NULL; + } + bufsz -= FU_SYNAPROM_FIRMWARE_SIGSIZE; + + /* parse each chunk */ + while (offset != bufsz) { + FuSynapromFirmwareHdr header; + guint32 hdrsz; + g_autofree FuSynapromFirmwareItem *item = NULL; + + /* verify item header */ + memcpy (&header, buf, sizeof(header)); + item = g_new0 (FuSynapromFirmwareItem, 1); + item->tag = GUINT16_FROM_LE(header.tag); + if (item->tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "tag 0x%04x is too large", + item->tag); + return NULL; + } + hdrsz = GUINT32_FROM_LE(header.bufsz); + offset += sizeof(header) + hdrsz; + if (offset > bufsz) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "data is corrupted 0x%04x > 0x%04x", + (guint) offset, (guint) bufsz); + return NULL; + } + + /* move pointer to data */ + buf += sizeof(header); + item->bytes = g_bytes_new (buf, hdrsz); + g_debug ("adding 0x%04x (%s) with size 0x%04x", + item->tag, + fu_synaprom_firmware_tag_to_string (item->tag), + hdrsz); + g_ptr_array_add (firmware, g_steal_pointer (&item)); + + /* next item */ + buf += hdrsz; + } + return g_steal_pointer (&firmware); +} + +GBytes * +fu_synaprom_firmware_get_bytes_by_tag (GPtrArray *firmware, guint16 tag, GError **error) +{ + for (guint i = 0; i < firmware->len; i++) { + FuSynapromFirmwareItem *item = g_ptr_array_index (firmware, i); + if (item->tag == tag) + return g_bytes_ref (item->bytes); + } + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "no item with tag 0x%04x", tag); + return NULL; +} + +GBytes * +fu_synaprom_firmware_generate (void) +{ + GByteArray *blob = g_byte_array_new (); + const guint8 data[] = { 'R', 'H' }; + FuSynapromFirmwareMfwHeader hdr = { + .product = GUINT32_TO_LE(0x41), + .id = GUINT32_TO_LE(0xff), + .buildtime = GUINT32_TO_LE(0xff), + .buildnum = GUINT32_TO_LE(0xff), + .vmajor = 10, + .vminor = 1, + }; + guint16 tag1 = GUINT16_TO_LE(FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER); + guint16 tag2 = GUINT16_TO_LE(FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD); + guint32 hdrsz = GUINT32_TO_LE(sizeof(hdr)); + guint32 datasz = GUINT32_TO_LE(sizeof(data)); + + /* add header */ + g_byte_array_append (blob, (const guint8 *) &tag1, sizeof(tag1)); + g_byte_array_append (blob, (const guint8 *) &hdrsz, sizeof(hdrsz)); + g_byte_array_append (blob, (const guint8 *) &hdr, sizeof(hdr)); + + /* add payload */ + g_byte_array_append (blob, (const guint8 *) &tag2, sizeof(tag2)); + g_byte_array_append (blob, (const guint8 *) &datasz, sizeof(datasz)); + g_byte_array_append (blob, data, sizeof(data)); + + /* add signature */ + for (guint i = 0; i < FU_SYNAPROM_FIRMWARE_SIGSIZE; i++) { + guint8 sig = 0xff; + g_byte_array_append (blob, &sig, 1); + } + return g_byte_array_free_to_bytes (blob); +} diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-firmware.h fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-firmware.h --- fwupd-1.0.9/plugins/synaptics-prometheus/fu-synaprom-firmware.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/fu-synaprom-firmware.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER 0x0001 +#define FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD 0x0002 +#define FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER 0x0003 +#define FU_SYNAPROM_FIRMWARE_TAG_CFG_PAYLOAD 0x0004 + +/* le */ +typedef struct __attribute__((packed)) { + guint32 product; + guint32 id; /* MFW unique id used for compat verification */ + guint32 buildtime; /* unix-style build time */ + guint32 buildnum; /* build number */ + guint8 vmajor; /* major version */ + guint8 vminor; /* minor version */ + guint8 unused[6]; +} FuSynapromFirmwareMfwHeader; + +/* le */ +typedef struct __attribute__((packed)) { + guint32 product; + guint32 id1; /* verification ID */ + guint32 id2; /* verification ID */ + guint16 version; /* config version */ + guint8 unused[2]; +} FuSynapromFirmwareCfgHeader; + +GPtrArray *fu_synaprom_firmware_new (GBytes *blob, + GError **error); +GBytes *fu_synaprom_firmware_get_bytes_by_tag (GPtrArray *firmware, + guint16 tag, + GError **error); +GBytes *fu_synaprom_firmware_generate (void); diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/meson.build fwupd-1.2.10/plugins/synaptics-prometheus/meson.build --- fwupd-1.0.9/plugins/synaptics-prometheus/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,75 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsPrometheus"'] + +install_data(['synaptics-prometheus.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_synaptics_prometheus', + fu_hash, + sources : [ + 'fu-plugin-synaptics-prometheus.c', + 'fu-synaprom-common.c', + 'fu-synaprom-config.c', + 'fu-synaprom-device.c', + 'fu-synaprom-firmware.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) + +if get_option('tests') + testdatadir = join_paths(meson.current_source_dir(), 'data') + cargs += '-DTESTDATADIR="' + testdatadir + '"' + e = executable( + 'synaptics-prometheus-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-synaprom-common.c', + 'fu-synaprom-config.c', + 'fu-synaprom-device.c', + 'fu-synaprom-firmware.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + dependencies : [ + plugin_deps, + ], + link_with : [ + libfwupdprivate, + ], + c_args : cargs + ) + test('synaptics-prometheus-self-test', e) + + # for fuzzing + executable( + 'synaptics-prometheus-dump', + sources : [ + 'fu-dump.c', + 'fu-synaprom-firmware.c', + ], + include_directories : [ + include_directories('../..'), + ], + dependencies : [ + gio, + ], + c_args : cargs + ) +endif diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/README.md fwupd-1.2.10/plugins/synaptics-prometheus/README.md --- fwupd-1.0.9/plugins/synaptics-prometheus/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,26 @@ +Synaptics Prometheus +==================== + +Introduction +------------ + +This plugin can flash the firmware on the Synaptics Prometheus fingerprint readers. + +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.synaptics.prometheus + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_06CB&PID_00A9&REV_0001` + * `USB\VID_06CB&PID_00A9` diff -Nru fwupd-1.0.9/plugins/synaptics-prometheus/synaptics-prometheus.quirk fwupd-1.2.10/plugins/synaptics-prometheus/synaptics-prometheus.quirk --- fwupd-1.0.9/plugins/synaptics-prometheus/synaptics-prometheus.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/synaptics-prometheus/synaptics-prometheus.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,7 @@ +[DeviceInstanceId=USB\VID_06CB&PID_00A9] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[DeviceInstanceId=USB\VID_06CB&PID_00BD] +Plugin = synaptics_prometheus +InstallDuration = 2 diff -Nru fwupd-1.0.9/plugins/test/fu-plugin-test.c fwupd-1.2.10/plugins/test/fu-plugin-test.c --- fwupd-1.0.9/plugins/test/fu-plugin-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/test/fu-plugin-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,7 +6,6 @@ #include "config.h" -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" struct FuPluginData { @@ -17,6 +15,11 @@ 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"); } @@ -42,7 +45,7 @@ fu_device_set_vendor (device, "ACME Corp."); fu_device_set_vendor_id (device, "USB:0x046D"); fu_device_set_version_bootloader (device, "0.1.2"); - fu_device_set_version (device, "1.2.2"); + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version_lowest (device, "1.2.0"); if (g_strcmp0 (g_getenv ("FWUPD_PLUGIN_TEST"), "registration") == 0) { fu_plugin_device_register (plugin, device); @@ -55,6 +58,33 @@ } } fu_plugin_device_add (plugin, device); + + if (g_strcmp0 (g_getenv ("FWUPD_PLUGIN_TEST"), "composite") == 0) { + g_autoptr(FuDevice) child1 = NULL; + g_autoptr(FuDevice) child2 = NULL; + + child1 = fu_device_new (); + fu_device_set_physical_id (child1, "fake"); + fu_device_set_logical_id (child1, "child1"); + fu_device_add_guid (child1, "7fddead7-12b5-4fb9-9fa0-6d30305df755"); + fu_device_set_name (child1, "Module1"); + fu_device_set_version (child1, "1", FWUPD_VERSION_FORMAT_PLAIN); + fu_device_add_parent_guid (child1, "b585990a-003e-5270-89d5-3705a17f9a43"); + fu_device_add_flag (child1, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_plugin_device_add (plugin, child1); + + child2 = fu_device_new (); + fu_device_set_physical_id (child2, "fake"); + fu_device_set_logical_id (child2, "child2"); + fu_device_add_guid (child2, "b8fe6b45-8702-4bcd-8120-ef236caac76f"); + fu_device_set_name (child2, "Module2"); + fu_device_set_version (child2, "10", FWUPD_VERSION_FORMAT_PLAIN); + fu_device_add_parent_guid (child2, "b585990a-003e-5270-89d5-3705a17f9a43"); + fu_device_add_flag (child2, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_plugin_device_add (plugin, child2); + + } + return TRUE; } @@ -94,7 +124,9 @@ FwupdInstallFlags flags, GError **error) { - if (g_strcmp0 (g_getenv ("FWUPD_PLUGIN_TEST"), "fail") == 0) { + const gchar *test = g_getenv ("FWUPD_PLUGIN_TEST"); + gboolean requires_activation = g_strcmp0 (test, "requires-activation") == 0; + if (g_strcmp0 (test, "fail") == 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -117,12 +149,45 @@ fu_device_set_progress (device, i); } + /* composite test, upgrade composite devices */ + if (g_strcmp0 (test, "composite") == 0) { + if (g_strcmp0 (fu_device_get_logical_id (device), "child1") == 0) { + fu_device_set_version (device, "2", FWUPD_VERSION_FORMAT_PLAIN); + return TRUE; + } else if (g_strcmp0 (fu_device_get_logical_id (device), "child2") == 0) { + fu_device_set_version (device, "11", FWUPD_VERSION_FORMAT_PLAIN); + return TRUE; + } + } + /* upgrade, or downgrade */ - if (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { - fu_device_set_version (device, "1.2.2"); + if (requires_activation) { + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); } else { - fu_device_set_version (device, "1.2.3"); + if (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); + } else { + fu_device_set_version (device, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); + } + } + + /* do this all over again */ + if (g_strcmp0 (test, "another-write-required") == 0) { + g_unsetenv ("FWUPD_PLUGIN_TEST"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); } + + /* for the self tests only */ + fu_device_set_metadata_integer (device, "nr-update", + fu_device_get_metadata_integer (device, "nr-update") + 1); + + return TRUE; +} + +gboolean +fu_plugin_activate (FuPlugin *plugin, FuDevice *device, GError **error) +{ + fu_device_set_version (device, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); return TRUE; } @@ -133,3 +198,31 @@ fu_device_set_update_error (device, NULL); return TRUE; } + +gboolean +fu_plugin_composite_prepare (FuPlugin *plugin, + GPtrArray *devices, + GError **error) +{ + if (g_strcmp0 (g_getenv ("FWUPD_PLUGIN_TEST"), "composite") == 0) { + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + fu_device_set_metadata (device, "frimbulator", "1"); + } + } + return TRUE; +} + +gboolean +fu_plugin_composite_cleanup (FuPlugin *plugin, + GPtrArray *devices, + GError **error) +{ + if (g_strcmp0 (g_getenv ("FWUPD_PLUGIN_TEST"), "composite") == 0) { + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + fu_device_set_metadata (device, "frombulator", "1"); + } + } + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/test/meson.build fwupd-1.2.10/plugins/test/meson.build --- fwupd-1.0.9/plugins/test/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/test/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -6,6 +6,7 @@ endif shared_module('fu_plugin_test', + fu_hash, sources : [ 'fu-plugin-test.c', ], @@ -16,6 +17,9 @@ ], install : install_dummy, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff -Nru fwupd-1.0.9/plugins/test/README.md fwupd-1.2.10/plugins/test/README.md --- fwupd-1.0.9/plugins/test/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/test/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -5,3 +5,9 @@ ------------ 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 -Nru fwupd-1.0.9/plugins/thunderbolt/fu-plugin-thunderbolt.c fwupd-1.2.10/plugins/thunderbolt/fu-plugin-thunderbolt.c --- fwupd-1.0.9/plugins/thunderbolt/fu-plugin-thunderbolt.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/fu-plugin-thunderbolt.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Christian J. Kellner * * SPDX-License-Identifier: LGPL-2.1+ @@ -15,15 +14,9 @@ #include #include -#include -#include -#include - -#include "fu-plugin-thunderbolt.h" #include "fu-plugin-vfuncs.h" #include "fu-device-metadata.h" #include "fu-thunderbolt-image.h" -#include "fu-thunderbolt-known-devices.h" #ifndef HAVE_GUDEV_232 #pragma clang diagnostic push @@ -32,7 +25,8 @@ #pragma clang diagnostic pop #endif -#define TBT_NVM_RETRY_TIMEOUT 200 /* ms */ +#define TBT_NVM_RETRY_TIMEOUT 200 /* ms */ +#define FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT 60000 /* ms */ typedef void (*UEventNotify) (FuPlugin *plugin, GUdevDevice *udevice, @@ -41,16 +35,6 @@ struct FuPluginData { GUdevClient *udev; - - /* in the case we are updating */ - UEventNotify update_notify; - gpointer update_data; - - /* the timeout we wait for the device - * to be updated to re-appear, in ms. - * defaults to: - * FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT_MS */ - guint timeout; }; static gchar * @@ -70,13 +54,13 @@ return fu_plugin_thunderbolt_gen_id_from_syspath (syspath); } -static guint64 +static gboolean udev_device_get_sysattr_guint64 (GUdevDevice *device, const gchar *name, + guint64 *val_out, GError **error) { const gchar *sysfs; - guint64 val; sysfs = g_udev_device_get_sysfs_attr (device, name); if (sysfs == NULL) { @@ -84,19 +68,19 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed get id %s for %s", name, sysfs); - return 0x0; + return FALSE; } - val = g_ascii_strtoull (sysfs, NULL, 16); - if (val == 0x0) { + *val_out = g_ascii_strtoull (sysfs, NULL, 16); + if (*val_out == 0x0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to parse %s", sysfs); - return 0x0; + return FALSE; } - return val; + return TRUE; } static guint16 @@ -105,11 +89,10 @@ GError **error) { - guint64 id; + guint64 id = 0; - id = udev_device_get_sysattr_guint64 (device, name, error); - if (id == 0x0) - return id; + if (!udev_device_get_sysattr_guint64 (device, name, &id, error)) + return 0x0; if (id > G_MAXUINT16) { g_set_error (error, @@ -210,40 +193,45 @@ 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_plugin_thunderbolt_controller_is_native (controller_fw, - is_native, - error); + return fu_thunderbolt_image_controller_is_native (controller_fw, + is_native, + error); } -static void -fu_plugin_thunderbolt_add_known_parents (FuDevice *device, guint16 vid, guint16 did) +static gboolean +fu_plugin_thunderbolt_can_update (GUdevDevice *udevice) { - const gchar *parent = NULL; + g_autoptr(GError) nvmem_error = NULL; + g_autoptr(GFile) non_active_nvmem = NULL; - if (vid == THUNDERBOLT_VENDOR_DELL) { - if (did == THUNDERBOLT_DEVICE_DELL_TB16_CABLE || - did == THUNDERBOLT_DEVICE_DELL_TB16_DOCK) - parent = PARENT_GUID_DELL_TB16; + 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; } - if (parent != NULL ) { - fu_device_add_parent_guid (device, parent); - g_debug ("Add known parent %s to %u:%u", parent, vid, did); - } + return TRUE; } static void @@ -267,6 +255,7 @@ g_autoptr(FuDevice) dev = NULL; g_autoptr(GError) error_vid = NULL; g_autoptr(GError) error_did = NULL; + g_autoptr(GError) error_setup = NULL; uuid = g_udev_device_get_sysfs_attr (device, "unique_id"); if (uuid == NULL) { @@ -327,26 +316,34 @@ 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); - fu_plugin_thunderbolt_add_known_parents (dev, vid, did); + } else { + fu_device_set_update_error (dev, "Device is in safe mode"); } - fu_device_set_platform_id (dev, uuid); + fu_device_set_physical_id (dev, uuid); fu_device_set_metadata (dev, "sysfs-path", devpath); name = g_udev_device_get_sysfs_attr (device, "device_name"); @@ -366,20 +363,31 @@ fu_device_add_icon (dev, "audio-card"); } + fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin)); vendor = g_udev_device_get_sysfs_attr (device, "vendor_name"); if (vendor != NULL) fu_device_set_vendor (dev, vendor); if (vendor_id != NULL) fu_device_set_vendor_id (dev, vendor_id); if (device_id != NULL) - fu_device_add_guid (dev, device_id); + fu_device_add_instance_id (dev, device_id); if (version != NULL) - fu_device_set_version (dev, version); + fu_device_set_version (dev, version, FWUPD_VERSION_FORMAT_PAIR); if (is_host) fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); + /* we never open the device, so convert the instance IDs */ + if (!fu_device_setup (dev, &error_setup)) { + g_warning ("failed to setup: %s", error_setup->message); + return; + } 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 @@ -397,6 +405,7 @@ * power on supported devices even when in low power mode -- * this will happen in coldplug_prepare and prepare_for_update */ if (fu_plugin_thunderbolt_is_host (device) && + !fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) && fu_device_get_metadata_boolean (dev, FU_DEVICE_METADATA_TBT_CAN_FORCE_POWER)) { g_debug ("ignoring remove event as force powered"); return; @@ -422,7 +431,7 @@ } version = fu_plugin_thunderbolt_udev_get_version (device); - fu_device_set_version (dev, version); + fu_device_set_version (dev, version, FWUPD_VERSION_FORMAT_PAIR); } static gboolean @@ -432,20 +441,12 @@ gpointer user_data) { FuPlugin *plugin = (FuPlugin *) user_data; - FuPluginData *data = fu_plugin_get_data (plugin); if (action == NULL) return TRUE; g_debug ("uevent for %s: %s", g_udev_device_get_sysfs_path (device), action); - if (data->update_notify != NULL) { - g_debug ("using update notify handler for uevent"); - - data->update_notify (plugin, device, action, data->update_data); - return TRUE; - } - if (g_str_equal (action, "add")) { fu_plugin_thunderbolt_add (plugin, device); } else if (g_str_equal (action, "remove")) { @@ -475,10 +476,7 @@ 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 @@ -509,7 +507,7 @@ if (n < 1 && errno != EINTR) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), - "could write to 'nvm_authenticate': %s", + "could not write to 'nvm_authenticate': %s", g_strerror (errno)); (void) close (fd); return FALSE; @@ -520,7 +518,7 @@ if (r < 0 && errno != EINTR) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), - "could close 'nvm_authenticate': %s", + "could not close 'nvm_authenticate': %s", g_strerror (errno)); return FALSE; } @@ -586,192 +584,6 @@ return g_output_stream_close (os, NULL, error); } -typedef struct UpdateData { - - gboolean have_device; - GMainLoop *mainloop; - const gchar *target_uuid; - guint timeout_id; - - GHashTable *changes; -} UpdateData; - -static gboolean -on_wait_for_device_timeout (gpointer user_data) -{ - UpdateData *data = (UpdateData *) user_data; - g_main_loop_quit (data->mainloop); - data->timeout_id = 0; - return FALSE; -} - -static void -on_wait_for_device_added (FuPlugin *plugin, - GUdevDevice *device, - UpdateData *up_data) -{ - FuDevice *dev; - const gchar *uuid; - const gchar *path; - g_autofree gchar *version = NULL; - g_autofree gchar *id = NULL; - - uuid = g_udev_device_get_sysfs_attr (device, "unique_id"); - if (uuid == NULL) - return; - - dev = g_hash_table_lookup (up_data->changes, uuid); - if (dev == NULL) { - /* a previously unknown device, add it via - * the normal way */ - fu_plugin_thunderbolt_add (plugin, device); - return; - } - - /* ensure the device path is correct */ - path = g_udev_device_get_sysfs_path (device); - fu_device_set_metadata (dev, "sysfs-path", path); - - /* make sure the version is correct, might have changed - * after update. */ - version = fu_plugin_thunderbolt_udev_get_version (device); - fu_device_set_version (dev, version); - - id = fu_plugin_thunderbolt_gen_id (device); - fu_plugin_cache_add (plugin, id, dev); - - g_hash_table_remove (up_data->changes, uuid); - - /* check if this device is the target*/ - if (g_str_equal (uuid, up_data->target_uuid)) { - up_data->have_device = TRUE; - g_debug ("target (%s) re-appeared", uuid); - g_main_loop_quit (up_data->mainloop); - } -} - -static void -on_wait_for_device_removed (FuPlugin *plugin, - GUdevDevice *device, - UpdateData *up_data) -{ - g_autofree gchar *id = NULL; - FuDevice *dev; - const gchar *uuid; - - id = fu_plugin_thunderbolt_gen_id (device); - dev = fu_plugin_cache_lookup (plugin, id); - - if (dev == NULL) - return; - - fu_plugin_cache_remove (plugin, id); - uuid = fu_device_get_platform_id (dev); - g_hash_table_insert (up_data->changes, - (gpointer) uuid, - g_object_ref (dev)); - - /* check if this device is the target */ - if (g_str_equal (uuid, up_data->target_uuid)) { - up_data->have_device = FALSE; - g_debug ("target (%s) disappeared", uuid); - } -} - -static void -on_wait_for_device_notify (FuPlugin *plugin, - GUdevDevice *device, - const char *action, - gpointer user_data) -{ - UpdateData *up_data = (UpdateData *) user_data; - - /* nb: action cannot be NULL since we are only called from - * udev_event_cb, which ensures that */ - if (g_str_equal (action, "add")) { - on_wait_for_device_added (plugin, device, up_data); - } else if (g_str_equal (action, "remove")) { - on_wait_for_device_removed (plugin, device, up_data); - } else if (g_str_equal (action, "change")) { - fu_plugin_thunderbolt_change (plugin, device); - } -} - -static void -remove_leftover_devices (gpointer key, - gpointer value, - gpointer user_data) -{ - FuPlugin *plugin = FU_PLUGIN (user_data); - FuDevice *dev = FU_DEVICE (value); - const gchar *syspath = fu_device_get_metadata (dev, "sysfs-path"); - g_autofree gchar *id = NULL; - - id = fu_plugin_thunderbolt_gen_id_from_syspath (syspath); - - fu_plugin_cache_remove (plugin, id); - fu_plugin_device_remove (plugin, dev); -} - -static gboolean -fu_plugin_thunderbolt_wait_for_device (FuPlugin *plugin, - FuDevice *dev, - GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - UpdateData up_data = { TRUE, }; - g_autoptr(GMainLoop) mainloop = NULL; - g_autoptr(GHashTable) changes = NULL; - - up_data.mainloop = mainloop = g_main_loop_new (NULL, FALSE); - up_data.target_uuid = fu_device_get_platform_id (dev); - - /* this will limit the maximum amount of time we wait for - * the device (i.e. 'dev') to re-appear. */ - up_data.timeout_id = g_timeout_add (data->timeout, - on_wait_for_device_timeout, - &up_data); - - /* this will capture the device added, removed, changed - * signals while we are updating. */ - data->update_data = &up_data; - data->update_notify = on_wait_for_device_notify; - - changes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); - up_data.changes = changes; - - /* now we wait ... */ - g_main_loop_run (mainloop); - - /* restore original udev change handler */ - data->update_data = NULL; - data->update_notify = NULL; - - if (up_data.timeout_id > 0) - g_source_remove (up_data.timeout_id); - - if (!up_data.have_device) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "timed out while waiting for device"); - return FALSE; - } - - g_hash_table_foreach (changes, remove_leftover_devices, plugin); - - return TRUE; -} - -/* internal interface */ - -void -fu_plugin_thunderbolt_set_timeout (FuPlugin *plugin, guint timeout_ms) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - data->timeout = timeout_ms; -} - /* virtual functions */ void @@ -780,11 +592,14 @@ 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); - data->timeout = FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT_MS; + /* dell-dock plugin uses a slower bus for flashing */ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "dell_dock"); } void @@ -833,10 +648,10 @@ { FuPluginData *data = fu_plugin_get_data (plugin); const gchar *devpath; - guint64 status; g_autoptr(GUdevDevice) udevice = NULL; g_autoptr(GError) error_local = NULL; - gboolean force = (flags & FWUPD_INSTALL_FLAG_FORCE) != 0; + gboolean install_force = (flags & FWUPD_INSTALL_FLAG_FORCE) != 0; + gboolean device_ignore_validation = fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_IGNORE_VALIDATION); FuPluginValidation validation; devpath = fu_device_get_metadata (dev, "sysfs-path"); @@ -868,7 +683,7 @@ default: break; } - if (!force) { + if (!install_force && !device_ignore_validation) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -881,41 +696,65 @@ } fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); - if (!fu_plugin_thunderbolt_write_firmware (dev, udevice, blob_fw, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "could not write firmware to thunderbolt device at %s: %s", - devpath, error_local->message); + if (!fu_plugin_thunderbolt_write_firmware (dev, udevice, blob_fw, error)) { + g_prefix_error (error, + "could not write firmware to thunderbolt device at %s: ", + devpath); return FALSE; } - if (!fu_plugin_thunderbolt_trigger_update (udevice, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Could not start thunderbolt device upgrade: %s", - error_local->message); + if (!fu_plugin_thunderbolt_trigger_update (udevice, error)) { + g_prefix_error (error, "could not start thunderbolt device upgrade: "); return FALSE; } fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART); + fu_device_set_remove_delay (dev, FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + return TRUE; +} + +gboolean +fu_plugin_update_attach (FuPlugin *plugin, + FuDevice *dev, + GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + const gchar *devpath; + const gchar *attribute; + guint64 status; + g_autoptr(GUdevDevice) udevice = NULL; - /* the device will disappear and we need to wait until it reappears, - * and then check if we find an error */ - if (!fu_plugin_thunderbolt_wait_for_device (plugin, dev, &error_local)) { + devpath = fu_device_get_metadata (dev, "sysfs-path"); + udevice = g_udev_client_query_by_sysfs_path (data->udev, devpath); + if (udevice == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, - "could not detect device after update: %s", - error_local->message); + "could not find thunderbolt device at %s", + devpath); return FALSE; } /* now check if the update actually worked */ - status = udev_device_get_sysattr_guint64 (udevice, - "nvm_authenticate", - &error_local); + attribute = g_udev_device_get_sysfs_attr (udevice, "nvm_authenticate"); + if (attribute == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to find nvm_authenticate attribute for %s", + fu_device_get_name (dev)); + return FALSE; + } + status = g_ascii_strtoull (attribute, NULL, 16); + 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", + g_strerror (errno)); + return FALSE; + } /* anything else then 0x0 means we got an error */ if (status != 0x0) { diff -Nru fwupd-1.0.9/plugins/thunderbolt/fu-plugin-thunderbolt.h fwupd-1.2.10/plugins/thunderbolt/fu-plugin-thunderbolt.h --- fwupd-1.0.9/plugins/thunderbolt/fu-plugin-thunderbolt.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/fu-plugin-thunderbolt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Christian J. Kellner - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_PLUGIN_THUNDERBOLT_H__ -#define __FU_PLUGIN_THUNDERBOLT_H__ - -#include "fu-plugin.h" - -#define FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT_MS 60 * 1000 - -void fu_plugin_thunderbolt_set_timeout (FuPlugin *plugin, - guint timeout_ms); - -#endif /* __FU_PLUGIN_THUNDERBOLT_H__ */ diff -Nru fwupd-1.0.9/plugins/thunderbolt/fu-self-test.c fwupd-1.2.10/plugins/thunderbolt/fu-self-test.c --- fwupd-1.0.9/plugins/thunderbolt/fu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Christian J. Kellner * * SPDX-License-Identifier: LGPL-2.1+ @@ -25,7 +24,6 @@ #include #include "fu-plugin-private.h" -#include "fu-plugin-thunderbolt.h" #include "fu-thunderbolt-image.h" #include "fu-test.h" @@ -458,7 +456,7 @@ { SyncContext *ctx = (SyncContext *) user_data; MockTree *tree = ctx->tree; - const gchar *uuid = fu_device_get_platform_id (device); + const gchar *uuid = fu_device_get_physical_id (device); MockTree *target; target = (MockTree *) mock_tree_find_uuid (tree, uuid); @@ -479,7 +477,7 @@ { SyncContext *ctx = (SyncContext *) user_data; MockTree *tree = ctx->tree; - const gchar *uuid = fu_device_get_platform_id (device); + const gchar *uuid = fu_device_get_physical_id (device); MockTree *target; target = (MockTree *) mock_tree_find_uuid (tree, uuid); @@ -539,7 +537,7 @@ { AttachContext *ctx = (AttachContext *) user_data; MockTree *tree = ctx->tree; - const gchar *uuid = fu_device_get_platform_id (device); + const gchar *uuid = fu_device_get_physical_id (device); MockTree *target; target = (MockTree *) mock_tree_find_uuid (tree, uuid); @@ -901,8 +899,7 @@ } if (!umockdev_in_mock_environment ()) { - g_warning ("Need to run within umockdev wrapper (umockdev-wrapper %s)!", - program_invocation_short_name); + g_warning ("Need to run with umockdev-wrapper"); return; } @@ -1048,40 +1045,40 @@ 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); @@ -1134,7 +1131,16 @@ /* simulate an update, where the device goes away and comes back * after the time in the last parameter (given in ms) */ up_ctx = mock_tree_prepare_for_update (tree, plugin, "42.23", fw_data, 1000); - ret = fu_plugin_runner_update (plugin, tree->fu_device, NULL, fw_data, 0, &error); + ret = fu_plugin_runner_update (plugin, tree->fu_device, fw_data, 0, &error); + g_assert_no_error (error); + g_assert_true (ret); + + /* we wait until the plugin has picked up all the + * subtree changes */ + ret = mock_tree_settle (tree, plugin); + g_assert_true (ret); + + ret = fu_plugin_runner_update_attach (plugin, tree->fu_device, &error); g_assert_no_error (error); g_assert_true (ret); @@ -1142,8 +1148,7 @@ g_debug ("version after update: %s", version_after); g_assert_cmpstr (version_after, ==, "42.23"); - /* we wait until the plugin has picked up all the - * subtree changes */ + /* make sure all pending events have happened */ ret = mock_tree_settle (tree, plugin); g_assert_true (ret); @@ -1175,21 +1180,29 @@ up_ctx = mock_tree_prepare_for_update (tree, plugin, "42.23", fw_data, 1000); up_ctx->result = UPDATE_FAIL_DEVICE_INTERNAL; - ret = fu_plugin_runner_update (plugin, tree->fu_device, NULL, fw_data, 0, &error); + ret = fu_plugin_runner_update (plugin, tree->fu_device, fw_data, 0, &error); + g_assert_no_error (error); + g_assert_true (ret); + + /* we wait until the plugin has picked up all the + * subtree changes, and make sure we still receive + * udev updates correctly and are in sync */ + ret = mock_tree_settle (tree, plugin); + g_assert_true (ret); + + ret = fu_plugin_runner_update_attach (plugin, tree->fu_device, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); g_assert_false (ret); + /* make sure all pending events have happened */ + ret = mock_tree_settle (tree, plugin); + g_assert_true (ret); + /* version should *not* have changed (but we get parsed version) */ version_after = fu_device_get_version (tree->fu_device); g_debug ("version after update: %s", version_after); g_assert_cmpstr (version_after, ==, tree->device->nvm_parsed_version); - /* we wait until the plugin has picked up all the - * subtree changes, and make sure we still receive - * udev updates correctly and are in sync */ - ret = mock_tree_settle (tree, plugin); - g_assert_true (ret); - ret = mock_tree_all (tree, mock_tree_node_have_fu_device, NULL); g_assert_true (ret); } @@ -1215,52 +1228,16 @@ up_ctx = mock_tree_prepare_for_update (tree, plugin, "42.23", fw_data, 1000); up_ctx->result = UPDATE_FAIL_DEVICE_NOSHOW; - /* lets make the plugin only wait a second for the device */ - fu_plugin_thunderbolt_set_timeout (plugin, 1000); - - ret = fu_plugin_runner_update (plugin, tree->fu_device, NULL, fw_data, 0, &error); - g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); - g_assert_false (ret); -} - -static void -test_update_fail_tooslow (ThunderboltTest *tt, gconstpointer user_data) -{ - FuPlugin *plugin = tt->plugin; - MockTree *tree = tt->tree; - GBytes *fw_data = tt->fw_data; - gboolean ret; - g_autoptr(GError) error = NULL; - g_autoptr(UpdateContext) up_ctx = NULL; - - /* test sanity check */ - g_assert_nonnull (tree); - g_assert_nonnull (fw_data); - - /* simulate an update, as in test_update_working, that is also working, - * but that takes longer then our timeout, i.e. set_timeout < last - * param in prepare_for_update. We will report an error, but we want - * to make sure that the device tree afterwards is still correct - */ - up_ctx = mock_tree_prepare_for_update (tree, plugin, "42.23", fw_data, 2000); - up_ctx->result = UPDATE_SUCCESS; - - /* lets make the plugin only wait half a second (1/4 of update time) */ - fu_plugin_thunderbolt_set_timeout (plugin, 500); - - ret = fu_plugin_runner_update (plugin, tree->fu_device, NULL, fw_data, 0, &error); - g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); - g_assert_false (ret); - - /* we wait until we see all devices again */ - ret = mock_tree_settle (tree, plugin); + ret = fu_plugin_runner_update (plugin, tree->fu_device, fw_data, 0, &error); + g_assert_no_error (error); g_assert_true (ret); + mock_tree_sync (tree, plugin, 500); + ret = mock_tree_all (tree, mock_tree_node_have_fu_device, NULL); - g_assert_true (ret); + g_assert_false (ret); } - int main (int argc, char **argv) { @@ -1311,12 +1288,5 @@ test_update_fail_nowshow, test_tear_down); - g_test_add ("/thunderbolt/update{failing-tooslow}", - ThunderboltTest, - TEST_INIT_FULL, - test_set_up, - test_update_fail_tooslow, - test_tear_down); - return g_test_run (); } diff -Nru fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-image.c fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-image.c --- fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-image.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-image.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Intel Corporation. * * SPDX-License-Identifier: LGPL-2.1+ @@ -643,9 +642,9 @@ } 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; @@ -780,16 +779,18 @@ } 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 -Nru fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-image.h fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-image.h --- fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-image.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-image.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Intel Corporation. * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_THUNDERBOLT_IMAGE_H__ -#define __FU_THUNDERBOLT_IMAGE_H__ +#pragma once #include @@ -16,12 +14,14 @@ 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); - -#endif /* __FU_THUNDERBOLT_IMAGE_H__ */ diff -Nru fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-known-devices.h fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-known-devices.h --- fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-known-devices.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-known-devices.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Dell Inc. - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_THUNDERBOLT_KNOWN_DEVICES_H__ -#define __FU_THUNDERBOLT_KNOWN_DEVICES_H__ - -#define THUNDERBOLT_VENDOR_DELL 0x00d4 - -/* Dell TB16 dock */ -#define THUNDERBOLT_DEVICE_DELL_TB16_CABLE 0xb051 -#define THUNDERBOLT_DEVICE_DELL_TB16_DOCK 0xb054 -#define PARENT_GUID_DELL_TB16 "e7ca1f36-bf73-4574-afe6-a4ccacabf479" - -#endif /* __FU_THUNDERBOLT_KNOWN_DEVICES_H__ */ diff -Nru fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-tool.c fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-tool.c --- fwupd-1.0.9/plugins/thunderbolt/fu-thunderbolt-tool.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/fu-thunderbolt-tool.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Intel Corporation. * * SPDX-License-Identifier: LGPL-2.1+ @@ -83,7 +82,7 @@ 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 -Nru fwupd-1.0.9/plugins/thunderbolt/meson.build fwupd-1.2.10/plugins/thunderbolt/meson.build --- fwupd-1.0.9/plugins/thunderbolt/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -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,10 +13,12 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, - gudev, ], ) @@ -23,6 +26,7 @@ testdatadir_dst = join_paths(meson.build_root(), 'data', 'tests') cargs += '-DTESTDATADIR="' + testdatadir_src + ':' + testdatadir_dst + '"' executable('tbtfwucli', + fu_hash, sources : [ 'fu-thunderbolt-tool.c', ], @@ -34,12 +38,10 @@ c_args : cargs, link_with : [ fu_plugin_thunderbolt, - fwupd, libfwupdprivate, ], dependencies : [ plugin_deps, - gudev, ], ) @@ -49,6 +51,7 @@ cargs += '-DPLUGINBUILDDIR="' + meson.current_build_dir() + '"' e = executable( 'thunderbolt-self-test', + fu_hash, sources : [ 'fu-self-test.c', 'fu-plugin-thunderbolt.c', @@ -61,16 +64,18 @@ ], dependencies : [ plugin_deps, - gudev, umockdev, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs ) test_env = environment() - test_env.prepend('LD_PRELOAD', 'libumockdev-preload.so.0') + if get_option('b_sanitize') == 'address' + test_env.prepend('LD_PRELOAD', 'libasan.so.5', 'libumockdev-preload.so.0', separator : ' ') + else + test_env.prepend('LD_PRELOAD', 'libumockdev-preload.so.0') + endif test('thunderbolt-self-test', e, env: test_env, timeout : 120) endif diff -Nru fwupd-1.0.9/plugins/thunderbolt/README.md fwupd-1.2.10/plugins/thunderbolt/README.md --- fwupd-1.0.9/plugins/thunderbolt/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -9,6 +9,28 @@ 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 -Nru fwupd-1.0.9/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c fwupd-1.2.10/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c --- fwupd-1.0.9/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt-power/fu-plugin-thunderbolt-power.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,5 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Dell Inc. +/* + * Copyright (C) 2017 Dell, Inc. * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -9,11 +8,8 @@ #include #include -#include #include -#include #include -#include #include "fu-plugin-vfuncs.h" #include "fu-device-metadata.h" @@ -324,6 +320,7 @@ /* 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 @@ -361,6 +358,7 @@ gboolean fu_plugin_update_prepare (FuPlugin *plugin, + FwupdInstallFlags flags, FuDevice *device, GError **error) { @@ -411,6 +409,7 @@ gboolean fu_plugin_update_cleanup (FuPlugin *plugin, + FwupdInstallFlags flags, FuDevice *device, GError **error) { @@ -427,8 +426,8 @@ return TRUE; } -gboolean -fu_plugin_coldplug (FuPlugin *plugin, GError **error) +static gboolean +fu_plugin_thunderbolt_power_coldplug (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); @@ -453,7 +452,13 @@ } gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + return fu_plugin_thunderbolt_power_coldplug (plugin, error); +} + +gboolean fu_plugin_recoldplug (FuPlugin *plugin, GError **error) { - return fu_plugin_coldplug (plugin, error); + return fu_plugin_thunderbolt_power_coldplug (plugin, error); } diff -Nru fwupd-1.0.9/plugins/thunderbolt-power/meson.build fwupd-1.2.10/plugins/thunderbolt-power/meson.build --- fwupd-1.0.9/plugins/thunderbolt-power/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/thunderbolt-power/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,6 +1,7 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginThunderboltPower"'] +cargs = ['-DG_LOG_DOMAIN="FuPluginThunderbolt"'] fu_plugin_thunderbolt_power = shared_module('fu_plugin_thunderbolt_power', + fu_hash, sources : [ 'fu-plugin-thunderbolt-power.c', ], @@ -11,9 +12,11 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, - gudev, ], ) diff -Nru fwupd-1.0.9/plugins/udev/fu-plugin-udev.c fwupd-1.2.10/plugins/udev/fu-plugin-udev.c --- fwupd-1.0.9/plugins/udev/fu-plugin-udev.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/udev/fu-plugin-udev.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,16 +6,16 @@ #include "config.h" -#include -#include - #include "fu-plugin.h" #include "fu-rom.h" #include "fu-plugin-vfuncs.h" -struct FuPluginData { - GUdevClient *gudev_client; -}; +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "pci"); +} gboolean fu_plugin_verify (FuPlugin *plugin, @@ -47,10 +46,11 @@ if (g_strcmp0 (fu_device_get_version (device), fu_rom_get_version (rom)) != 0) { g_debug ("changing version of %s from %s to %s", - fu_device_get_platform_id (device), + fu_device_get_id (device), fu_device_get_version (device), fu_rom_get_version (rom)); - fu_device_set_version (device, fu_rom_get_version (rom)); + fu_device_set_version (device, fu_rom_get_version (rom), + FWUPD_VERSION_FORMAT_UNKNOWN); } /* Also add the GUID from the firmware as the firmware may be more @@ -67,194 +67,38 @@ return TRUE; } -static gchar * -fu_plugin_udev_generate_vendor_id (GUdevDevice *device) -{ - const gchar *pci_id; - const gchar *subsys; - guint64 vid; - g_autofree gchar *subsys_up = NULL; - g_autofree gchar *vid_str = NULL; - - /* get upper cased subsystem */ - subsys = g_udev_device_get_subsystem (device); - if (subsys == NULL) - return NULL; - subsys_up = g_ascii_strup (subsys, -1); - - /* get vendor ID */ - pci_id = g_udev_device_get_property (device, "PCI_ID"); - if (pci_id != NULL) { - g_auto(GStrv) split = g_strsplit (pci_id, ":", 2); - vid_str = g_strdup (split[0]); - } - if (vid_str == NULL) { - g_warning ("no vendor ID for %s", - g_udev_device_get_sysfs_path (device)); - return NULL; - } - vid = g_ascii_strtoull (vid_str, NULL, 16); - if (vid == 0x0) { - g_warning ("failed to parse %s", vid_str); - return NULL; - } - return g_strdup_printf ("%s:0x%04X", subsys_up, (guint) vid); -} - -static void -fu_plugin_udev_add (FuPlugin *plugin, GUdevDevice *device) +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) { - FuDevice *dev_tmp; - const gchar *display_name; - const gchar *guid; - const gchar *id = NULL; - const gchar *product; - const gchar *vendor; + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + const gchar *guid = NULL; g_autofree gchar *rom_fn = NULL; - g_autofree gchar *vendor_id = NULL; - g_autofree gchar *version = NULL; - g_auto(GStrv) split = NULL; - g_autoptr(AsProfile) profile = as_profile_new (); - g_autoptr(AsProfileTask) ptask = NULL; - g_autoptr(FuDevice) dev = NULL; /* interesting device? */ - guid = g_udev_device_get_property (device, "FWUPD_GUID"); + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "pci") != 0) + return TRUE; + guid = g_udev_device_get_property (udev_device, "FWUPD_GUID"); if (guid == NULL) - return; - - /* get data */ - ptask = as_profile_start (profile, "FuPluginUdev:client-add{%s}", guid); - g_assert (ptask != NULL); - g_debug ("adding udev device: %s", g_udev_device_get_sysfs_path (device)); - - /* is already in database */ - id = g_udev_device_get_sysfs_path (device); - dev_tmp = fu_plugin_cache_lookup (plugin, id); - if (dev_tmp != NULL) { - g_debug ("ignoring duplicate %s", id); - return; - } + return TRUE; - /* get the FW version from the BCD device revision */ - product = g_udev_device_get_property (device, "PRODUCT"); - if (product != NULL) { - split = g_strsplit (product, "/", -1); - if (g_strv_length (split) != 3) { - g_warning ("env{PRODUCT} is invalid: %s", product); - return; - } - version = g_strdup (split[2]); - } + /* set the physical ID */ + if (!fu_udev_device_set_physical_id (device, "pci", error)) + return FALSE; /* did we get enough data */ - dev = fu_device_new (); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_set_platform_id (dev, id); - fu_device_add_guid (dev, guid); - fu_device_add_icon (dev, "audio-card"); - display_name = g_udev_device_get_property (device, "FWUPD_MODEL"); - if (display_name == NULL) - display_name = g_udev_device_get_property (device, "ID_MODEL_FROM_DATABASE"); - if (display_name != NULL) - fu_device_set_name (dev, display_name); - vendor = g_udev_device_get_property (device, "FWUPD_VENDOR"); - if (vendor == NULL) - vendor = g_udev_device_get_property (device, "ID_VENDOR_FROM_DATABASE"); - if (vendor != NULL) - fu_device_set_vendor (dev, vendor); - if (version != NULL) - fu_device_set_version (dev, version); - - /* set vendor ID */ - vendor_id = fu_plugin_udev_generate_vendor_id (device); - if (vendor_id != NULL) - fu_device_set_vendor_id (FU_DEVICE (dev), vendor_id); + 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 (g_udev_device_get_sysfs_path (device), "rom", NULL); + rom_fn = g_build_filename (fu_udev_device_get_sysfs_path (device), "rom", NULL); if (g_file_test (rom_fn, G_FILE_TEST_EXISTS)) - fu_device_set_metadata (dev, "RomFilename", rom_fn); + fu_device_set_metadata (FU_DEVICE (device), "RomFilename", rom_fn); - /* insert to hash */ - fu_plugin_cache_add (plugin, id, dev); - fu_plugin_device_add_delay (plugin, dev); -} - -static void -fu_plugin_udev_remove (FuPlugin *plugin, GUdevDevice *device) -{ - FuDevice *dev; - const gchar *id; - - /* interesting device? */ - if (g_udev_device_get_property (device, "FWUPD_GUID") == NULL) - return; - - /* already in database */ - id = g_udev_device_get_sysfs_path (device); - dev = fu_plugin_cache_lookup (plugin, id); - if (dev == NULL) - return; - fu_plugin_device_remove (plugin, dev); -} - -static void -fu_plugin_udev_uevent_cb (GUdevClient *gudev_client, - const gchar *action, - GUdevDevice *udev_device, - FuPlugin *plugin) -{ - if (g_strcmp0 (action, "remove") == 0) { - fu_plugin_udev_remove (plugin, udev_device); - return; - } - if (g_strcmp0 (action, "add") == 0) { - fu_plugin_udev_add (plugin, udev_device); - return; - } -} - -void -fu_plugin_init (FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); - const gchar *subsystems[] = { "pci", NULL }; - - data->gudev_client = g_udev_client_new (subsystems); - g_signal_connect (data->gudev_client, "uevent", - G_CALLBACK (fu_plugin_udev_uevent_cb), plugin); -} - -void -fu_plugin_destroy (FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - g_object_unref (data->gudev_client); -} + /* we never open the device, so convert the instance IDs */ + if (!fu_device_setup (FU_DEVICE (device), error)) + return FALSE; -gboolean -fu_plugin_coldplug (FuPlugin *plugin, GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - GList *devices; - GUdevDevice *udev_device; - const gchar *devclass[] = { "pci", NULL }; - g_autoptr(AsProfile) profile = as_profile_new (); - - /* get all devices of class */ - for (guint i = 0; devclass[i] != NULL; i++) { - g_autoptr(AsProfileTask) ptask = NULL; - ptask = as_profile_start (profile, "FuPluginUdev:coldplug{%s}", devclass[i]); - g_assert (ptask != NULL); - devices = g_udev_client_query_by_subsystem (data->gudev_client, - devclass[i]); - for (GList *l = devices; l != NULL; l = l->next) { - udev_device = l->data; - fu_plugin_udev_add (plugin, udev_device); - } - g_list_foreach (devices, (GFunc) g_object_unref, NULL); - g_list_free (devices); - } + /* insert to hash */ + fu_plugin_device_add (plugin, FU_DEVICE (device)); return TRUE; } diff -Nru fwupd-1.0.9/plugins/udev/fu-rom.c fwupd-1.2.10/plugins/udev/fu-rom.c --- fwupd-1.0.9/plugins/udev/fu-rom.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/udev/fu-rom.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -8,9 +7,6 @@ #include "config.h" #include -#include -#include -#include #include #include @@ -552,7 +548,6 @@ guint32 sz = buffer_sz; guint32 jump = 0; guint32 hdr_sz = 0; - g_autofree gchar *id = NULL; g_autoptr(GChecksum) checksum_sha1 = g_checksum_new (G_CHECKSUM_SHA1); g_autoptr(GChecksum) checksum_sha256 = g_checksum_new (G_CHECKSUM_SHA256); @@ -690,10 +685,8 @@ g_ptr_array_add (self->checksums, g_strdup (g_checksum_get_string (checksum_sha256))); /* 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); - g_debug ("using %s for %s", self->guid, id); + self->guid = g_strdup_printf ("PCI\\VEN_%04X&DEV_%04X", + self->vendor_id, self->device_id); /* not known */ if (self->version == NULL) { @@ -718,14 +711,10 @@ g_autofree gchar *fn = NULL; g_autofree guint8 *buffer = NULL; g_autoptr(GFileOutputStream) output_stream = NULL; - g_autoptr(AsProfile) profile = as_profile_new (); - g_autoptr(AsProfileTask) ptask = NULL; g_return_val_if_fail (FU_IS_ROM (self), FALSE); /* open file */ - ptask = as_profile_start_literal (profile, "FuRom:reading-data"); - g_assert (ptask != NULL); self->stream = G_INPUT_STREAM (g_file_read (file, cancellable, &error_local)); if (self->stream == NULL) { g_set_error_literal (error, diff -Nru fwupd-1.0.9/plugins/udev/fu-rom.h fwupd-1.2.10/plugins/udev/fu-rom.h --- fwupd-1.0.9/plugins/udev/fu-rom.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/udev/fu-rom.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_ROM_H -#define __FU_ROM_H +#pragma once -#include #include G_BEGIN_DECLS @@ -56,6 +53,3 @@ const gchar *fu_rom_kind_to_string (FuRomKind kind); G_END_DECLS - -#endif /* __FU_ROM_H */ - diff -Nru fwupd-1.0.9/plugins/udev/fu-rom-tool.c fwupd-1.2.10/plugins/udev/fu-rom-tool.c --- fwupd-1.0.9/plugins/udev/fu-rom-tool.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/udev/fu-rom-tool.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/plugins/udev/fu-self-test.c fwupd-1.2.10/plugins/udev/fu-self-test.c --- fwupd-1.0.9/plugins/udev/fu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/udev/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -8,7 +7,6 @@ #include "config.h" #include -#include #include #include #include diff -Nru fwupd-1.0.9/plugins/udev/meson.build fwupd-1.2.10/plugins/udev/meson.build --- fwupd-1.0.9/plugins/udev/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/udev/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -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,15 +13,18 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, - gudev, ], ) executable( 'fu-rom-tool', + fu_hash, sources : [ 'fu-rom-tool.c', 'fu-rom.c', @@ -32,10 +36,9 @@ ], dependencies : [ plugin_deps, - gudev, + libjsonglib, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs, @@ -48,6 +51,7 @@ cargs += '-DTESTDATADIR="' + testdatadir + '"' e = executable( 'udev-self-test', + fu_hash, sources : [ 'fu-self-test.c', 'fu-rom.c', @@ -59,10 +63,8 @@ ], dependencies : [ plugin_deps, - gudev, ], link_with : [ - fwupd, libfwupdprivate, ], c_args : cargs diff -Nru fwupd-1.0.9/plugins/udev/README.md fwupd-1.2.10/plugins/udev/README.md --- fwupd-1.0.9/plugins/udev/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/udev/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -10,3 +10,12 @@ 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 -Nru fwupd-1.0.9/plugins/uefi/efi/fwup-cleanups.h fwupd-1.2.10/plugins/uefi/efi/fwup-cleanups.h --- fwupd-1.0.9/plugins/uefi/efi/fwup-cleanups.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwup-cleanups.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 -Nru fwupd-1.0.9/plugins/uefi/efi/fwup-common.c fwupd-1.2.10/plugins/uefi/efi/fwup-common.c --- fwupd-1.0.9/plugins/uefi/efi/fwup-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwup-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/uefi/efi/fwup-common.h fwupd-1.2.10/plugins/uefi/efi/fwup-common.h --- fwupd-1.0.9/plugins/uefi/efi/fwup-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwup-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015-2016 Peter Jones + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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))) diff -Nru fwupd-1.0.9/plugins/uefi/efi/fwupdate.c fwupd-1.2.10/plugins/uefi/efi/fwupdate.c --- fwupd-1.0.9/plugins/uefi/efi/fwupdate.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwupdate.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,754 @@ +/* + * Copyright (C) 2014-2018 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include +#include + +#include "fwup-cleanups.h" +#include "fwup-common.h" +#include "fwup-efi.h" +#include "fwup-debug.h" + +#define UNUSED __attribute__((__unused__)) +#define GNVN_BUF_SIZE 1024 +#define FWUP_NUM_CAPSULE_UPDATES_MAX 128 + +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); +} + +_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 INTN +fwup_dp_size(EFI_DEVICE_PATH *dp, INTN limit) +{ + INTN ret = 0; + while (1) { + if (limit < 4) + break; + INTN nodelen = DevicePathNodeLength(dp); + if (nodelen > limit) + break; + limit -= nodelen; + ret += nodelen; + + if (IsDevicePathEnd(dp)) + return ret; + dp = NextDevicePathNode(dp); + } + return -1; +} + +static EFI_STATUS +fwup_populate_update_info(CHAR16 *name, FWUP_UPDATE_TABLE *info_out) +{ + EFI_STATUS rc; + FWUP_UPDATE_INFO *info = NULL; + UINTN info_size = 0; + UINT32 attrs = 0; + VOID *info_ptr = NULL; + + rc = fwup_get_variable(name, &fwupdate_guid, &info_ptr, &info_size, &attrs); + if (EFI_ERROR(rc)) + return rc; + info = (FWUP_UPDATE_INFO *)info_ptr; + + 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)) { + 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(FWUP_UPDATE_INFO, dp); + if (is > (INTN)info_size) { + 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 = fwup_dp_size(hdr, info_size); + if (sz < 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; + } + + info_out->info = info; + info_out->size = info_size; + 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 +fwup_populate_update_table(FWUP_UPDATE_TABLE **updates, UINTN *n_updates_out) +{ + EFI_GUID vendor_guid = empty_guid; + 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 = fwup_malloc0(GNVN_BUF_SIZE * 2); + if (variable_name == NULL) + return EFI_OUT_OF_RESOURCES; + + while (1) { + UINTN 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; + } + + /* not one of our state variables */ + if (CompareGuid(&vendor_guid, &fwupdate_guid)) + continue; + + /* ignore debugging settings */ + if (StrCmp(variable_name, L"FWUPDATE_VERBOSE") == 0 || + StrCmp(variable_name, L"FWUPDATE_DEBUG_LOG") == 0) + continue; + + if (n_updates > FWUP_NUM_CAPSULE_UPDATES_MAX) { + fwup_warning(L"Ignoring update %s", variable_name); + continue; + } + + 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)) { + 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) { + fwup_time(&update->info->time_attempted); + update->info->status = FWUPDATE_ATTEMPTED; + updates[n_updates++] = _steal_pointer(&update); + } + } + + *n_updates_out = n_updates; + return EFI_SUCCESS; +} + +static EFI_STATUS +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; + 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)) { + fwup_warning(L"Could not find handles"); + return rc; + } + + dp = *file_dp; + + fwup_debug(L"Searching Device Path: %s...", DevicePathToStr(dp)); + parent_dp = DuplicateDevicePath(dp); + if (parent_dp == NULL) + return EFI_INVALID_PARAMETER; + + dp = parent_dp; + count = 0; + while (1) { + if (IsDevicePathEnd(dp)) + return EFI_INVALID_PARAMETER; + + if (DevicePathType(dp) == MEDIA_DEVICE_PATH && + DevicePathSubType(dp) == MEDIA_FILEPATH_DP) + break; + + dp = NextDevicePathNode(dp); + ++count; + } + + SetDevicePathEndNode(dp); + fwup_debug(L"Device Path prepared: %s", DevicePathToStr(parent_dp)); + + for (UINTN i = 0; i < n_handles; i++) { + EFI_DEVICE_PATH *path; + + rc = uefi_call_wrapper(BS->HandleProtocol, 3, devices[i], &dpp, + (VOID **)&path); + if (EFI_ERROR(rc)) + continue; + + fwup_debug(L"Device supporting SFSP: %s", DevicePathToStr(path)); + + while (!IsDevicePathEnd(path)) { + fwup_debug(L"Comparing: %s and %s", + DevicePathToStr(parent_dp), + DevicePathToStr(path)); + + if (LibMatchDevicePaths(path, parent_dp) == TRUE) { + *fh = devices[i]; + for (UINTN j = 0; j < count; j++) + *file_dp = NextDevicePathNode(*file_dp); + + fwup_debug(L"Match up! Returning %s", + DevicePathToStr(*file_dp)); + return EFI_SUCCESS; + } + + path = NextDevicePathNode(path); + } + } + + fwup_warning(L"Failed to find '%s' DevicePath", DevicePathToStr(*file_dp)); + return EFI_UNSUPPORTED; +} + +static EFI_STATUS +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; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE_HANDLE root; + EFI_STATUS rc; + + rc = uefi_call_wrapper(BS->LocateDevicePath, 3, &sfsp, &file_dp, + (EFI_HANDLE *)&device); + if (EFI_ERROR(rc)) { + rc = fwup_search_file(&file_dp, &device); + if (EFI_ERROR(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) { + fwup_warning(L"Could not find appropriate device"); + return EFI_UNSUPPORTED; + } + + UINT16 sz16; + UINTN sz; + CopyMem(&sz16, &file_dp->Length[0], sizeof(sz16)); + sz = sz16; + sz -= 4; + 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; + } + + _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); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not open device interface: %r", rc); + return rc; + } + fwup_debug(L"Found device"); + + rc = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not open volume: %r", rc); + return rc; + } + fwup_debug(L"Found volume"); + + rc = uefi_call_wrapper(root->Open, 5, root, fh, filename, + EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not open file '%s': %r", filename, rc); + return rc; + } + fwup_debug(L"Found file"); + + return EFI_SUCCESS; +} + +static EFI_STATUS +fwup_delete_boot_order(CHAR16 *name, EFI_GUID guid) +{ + UINT16 boot_num; + EFI_STATUS rc; + UINTN info_size = 0; + 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 = fwup_get_variable(L"BootOrder", &guid, &info_ptr, &info_size, &attrs); + if (EFI_ERROR(rc)) + return rc; + + new_info_ptr = fwup_malloc(info_size); + if (new_info_ptr == NULL) + return EFI_OUT_OF_RESOURCES; + + 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++; + + } else { + num_found = TRUE; + } + } + + /* if not in the BootOrder list, do not update BootOrder */ + if (!num_found) + return EFI_SUCCESS; + + rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &guid, + attrs, new_list_num * sizeof(UINT16), + new_info_ptr); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not update variable status for '%s': %r", + name, rc); + return rc; + } + return rc; +} + +/* TODO: move to gnu-efi: https://github.com/vathpela/gnu-efi/issues/7 */ +static BOOLEAN +_StrHasPrefix(IN CONST CHAR16 *s1, IN CONST CHAR16 *s2) +{ + while (*s2) { + if (*s1 == L'\0' || *s1 != *s2) + return FALSE; + s1 += 1; + s2 += 1; + } + return TRUE; +} + +static EFI_STATUS +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 description + */ + 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; + EFI_STATUS status; + EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; + VOID *iface; + + status = LibLocateHandle(ByProtocol, &gop_guid, NULL, &num_handles, + &handles); + if (EFI_ERROR(status)) + return status; + + if (handles == NULL || num_handles == 0) + return EFI_UNSUPPORTED; + + for (UINTN i = 0; i < num_handles; i++) { + gop_handle = handles[i]; + + status = uefi_call_wrapper(BS->OpenProtocol, 6, + gop_handle, &gop_guid, &iface, + loaded_image, 0, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(status)) + continue; + + gop = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)iface; + + *mode = gop->Mode->Mode; + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + +static inline void +fwup_update_ux_capsule_checksum(UX_CAPSULE_HEADER *payload_hdr) +{ + UINT8 *buf = (UINT8 *)payload_hdr; + UINT8 sum = 0; + + payload_hdr->checksum = 0; + for (UINTN i = 0; i < sizeof(*payload_hdr); i++) + sum = (UINT8) (sum + buf[i]); + payload_hdr->checksum = sum; +} + +static EFI_STATUS +fwup_check_gop_for_ux_capsule(EFI_HANDLE loaded_image, + EFI_CAPSULE_HEADER *capsule) +{ + UX_CAPSULE_HEADER *payload_hdr; + EFI_STATUS rc; + + 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; + + fwup_update_ux_capsule_checksum(payload_hdr); + + return EFI_SUCCESS; +} + +static EFI_STATUS +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; + UINT8 *fbuf = NULL; + UINTN fsize = 0; + EFI_CAPSULE_HEADER *capsule; + + UINTN cbd_len; + EFI_PHYSICAL_ADDRESS cbd_data; + EFI_CAPSULE_HEADER *cap_out; + + rc = fwup_open_file((EFI_DEVICE_PATH *)update->info->dp_buf, &fh); + if (EFI_ERROR(rc)) + return rc; + + rc = fwup_read_file(fh, &fbuf, &fsize); + if (EFI_ERROR(rc)) + return rc; + + uefi_call_wrapper(fh->Close, 1, fh); + + if (fsize < sizeof(EFI_CAPSULE_HEADER)) { + fwup_warning(L"Invalid capsule size %d", fsize); + return EFI_INVALID_PARAMETER; + } + + 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; + } + + cbd_out->Length = cbd_len; + cbd_out->Union.DataBlock = cbd_data; + *capsule_out = cap_out; + + return EFI_SUCCESS; +} + +static EFI_STATUS +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; + + /* 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); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not query capsule capabilities: %r", rc); + return rc; + } + fwup_debug(L"QueryCapsuleCapabilities: %r max: %ld reset:%d", + rc, max_capsule_size, *reset); + fwup_debug(L"Capsules: %d", num_updates); + + fwup_msleep(1 * SECONDS); + rc = uefi_call_wrapper(RT->UpdateCapsule, 3, capsules, num_updates, + (EFI_PHYSICAL_ADDRESS)(UINTN)cbd); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not apply capsule update: %r", rc); + return rc; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS +fwup_set_update_statuses(FWUP_UPDATE_TABLE **updates) +{ + EFI_STATUS rc; + for (UINTN i = 0; i < FWUP_NUM_CAPSULE_UPDATES_MAX; i++) { + if (updates[i] == NULL || 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)) { + fwup_warning(L"Could not update variable status for '%s': %r", + updates[i]->name, rc); + return rc; + } + } + return EFI_SUCCESS; +} + +EFI_GUID SHIM_LOCK_GUID = + {0x605dab50,0xe046,0x4300,{0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23}}; + +static VOID +__attribute__((__optimize__("0"))) +fwup_debug_hook(VOID) +{ + EFI_GUID guid = SHIM_LOCK_GUID; + UINTN data = 0; + UINTN data_size = 1; + EFI_STATUS efi_status; + UINT32 attrs; + register volatile int x = 0; + extern char _text UNUSED, _data UNUSED; + + /* shim has done whatever is needed to get a debugger attached */ + efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"SHIM_DEBUG", + &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, &attrs, + &data_size, &data); + if (EFI_ERROR(efi_status) || data != 1) + return; + fwup_debug_set_enabled(TRUE); + return; + } + + fwup_debug_set_enabled(TRUE); + if (x) + return; + + x = 1; + 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; + 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, fwup_info info for our attached debugger */ + fwup_debug_hook(); + + /* step 1: find and validate update state variables */ + /* XXX TODO: + * 1) survey the reset types first, and separate into groups + * according to them + * 2) if there's more than one, mirror BootCurrent back into BootNext + * so we can do multiple runs + * 3) only select the ones from one type for the first go + */ + 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)) { + fwup_warning(L"Could not find updates: %r", rc); + return rc; + } + if (n_updates == 0) { + 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 */ + _cleanup_free EFI_CAPSULE_HEADER **capsules = NULL; + capsules = fwup_new0(EFI_CAPSULE_HEADER *, n_updates + 1); + EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_data; + 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 && + CompareGuid(&updates[i]->info->guid, &ux_capsule_guid) == 0) { + fwup_debug(L"GOP unsuitable: %r", rc); + continue; + } + fwup_warning(L"Could not build update list: %r", rc); + return rc; + } + j++; + } + n_updates = j; + 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 = fwup_set_update_statuses(updates); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not set update status: %r", rc); + return rc; + } + + /* step 4: apply the capsules */ + rc = fwup_apply_capsules(capsules, cbd_data, n_updates, &reset_type); + if (EFI_ERROR(rc)) { + fwup_warning(L"Could not apply capsules: %r", rc); + return rc; + } + + /* 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 -Nru fwupd-1.0.9/plugins/uefi/efi/fwup-debug.c fwupd-1.2.10/plugins/uefi/efi/fwup-debug.c --- fwupd-1.0.9/plugins/uefi/efi/fwup-debug.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwup-debug.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/uefi/efi/fwup-debug.h fwupd-1.2.10/plugins/uefi/efi/fwup-debug.h --- fwupd-1.0.9/plugins/uefi/efi/fwup-debug.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwup-debug.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015-2016 Peter Jones + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +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 ) diff -Nru fwupd-1.0.9/plugins/uefi/efi/fwup-efi.c fwupd-1.2.10/plugins/uefi/efi/fwup-efi.c --- fwupd-1.0.9/plugins/uefi/efi/fwup-efi.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwup-efi.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/uefi/efi/fwup-efi.h fwupd-1.2.10/plugins/uefi/efi/fwup-efi.h --- fwupd-1.0.9/plugins/uefi/efi/fwup-efi.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/fwup-efi.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#define FWUPDATE_ATTEMPT_UPDATE 0x00000001 +#define FWUPDATE_ATTEMPTED 0x00000002 + +#define UPDATE_INFO_VERSION 7 + +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 version; + UINT8 checksum; + UINT8 image_type; + UINT8 reserved; + UINT32 mode; + UINT32 x_offset; + UINT32 y_offset; +} __attribute__((__packed__)) UX_CAPSULE_HEADER; + +typedef struct { + UINT32 update_info_version; + + /* stuff we need to apply an update */ + EFI_GUID guid; + UINT32 capsule_flags; + UINT64 hw_inst; + + EFI_TIME time_attempted; + + /* our metadata */ + UINT32 status; + + /* variadic device path */ + union { + EFI_DEVICE_PATH dp; + UINT8 dp_buf[0]; + }; +} __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); diff -Nru fwupd-1.0.9/plugins/uefi/efi/meson.build fwupd-1.2.10/plugins/uefi/efi/meson.build --- fwupd-1.0.9/plugins/uefi/efi/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/efi/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,173 @@ +efi_cc = get_option('efi-cc') +efi_ld = get_option('efi-ld') +efi_ldsdir = get_option('efi-ldsdir') +efi_incdir = get_option('efi-includedir') + +gnu_efi_path_arch = '' +foreach name : [gnu_efi_arch, EFI_MACHINE_TYPE_NAME] + if (gnu_efi_path_arch == '' and name != '' and + cc.has_header('@0@/@1@/efibind.h'.format(efi_incdir, name))) + gnu_efi_path_arch = name + endif +endforeach + +if gnu_efi_path_arch != '' and EFI_MACHINE_TYPE_NAME == '' + error('gnu-efi is available, but EFI_MACHINE_TYPE_NAME is unknown') +endif + +efi_libdir = get_option('efi-libdir') +if efi_libdir == '' + cmd = 'cd /usr/lib/$(@0@ -print-multi-os-directory) && pwd'.format(efi_cc) + ret = run_command('sh', '-c', cmd) + if ret.returncode() == 0 + efi_libdir = ret.stdout().strip() + endif +endif + +have_gnu_efi = gnu_efi_path_arch != '' and efi_libdir != '' + +if not have_gnu_efi + error('gnu-efi support requested, but headers were not found') +endif + +arch_lds = 'elf_@0@_efi.lds'.format(gnu_efi_path_arch) +if efi_ldsdir == '' + efi_ldsdir = join_paths(efi_libdir, 'gnuefi') + cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds)) + if cmd.returncode() != 0 + efi_ldsdir = efi_libdir + cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds)) + if cmd.returncode() != 0 + error('Cannot find @0@'.format(arch_lds)) + endif + endif +endif + +message('efi-libdir: "@0@"'.format(efi_libdir)) +message('efi-ldsdir: "@0@"'.format(efi_ldsdir)) +message('efi-includedir: "@0@"'.format(efi_incdir)) + +debugdir = join_paths (libdir, 'debug') +compile_args = ['-Og', + '-g3', + '--param=ssp-buffer-size=4', + '-fexceptions', + '-Wall', + '-Wextra', + '-Wvla', + '-std=gnu11', + '-fpic', + '-fshort-wchar', + '-ffreestanding', + '-fno-strict-aliasing', + '-fno-stack-protector', + '-fno-stack-check', + '-fno-merge-constants', + '-Wsign-compare', + '-Wno-missing-field-initializers', + '-Wno-address-of-packed-member', + '-grecord-gcc-switches', + '-DDEBUGDIR="@0@"'.format(debugdir), + '-isystem', efi_incdir, + '-isystem', join_paths(efi_incdir, gnu_efi_path_arch)] +if get_option('werror') + compile_args += '-Werror' +endif +if efi_arch == 'x86_64' + compile_args += ['-mno-red-zone', + '-mno-sse', + '-mno-mmx', + '-DEFI_FUNCTION_WRAPPER', + '-DGNU_EFI_USE_MS_ABI'] +elif efi_arch == 'ia32' + compile_args += ['-mno-sse', + '-mno-mmx', + '-mno-red-zone', + '-m32'] +# no special cases for aarch64 or arm +endif + +efi_ldflags = ['-T', + join_paths(efi_ldsdir, arch_lds), + '-shared', + '-Bsymbolic', + '-nostdlib', + '-znocombreloc', + '-L', efi_libdir, + join_paths(efi_ldsdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))] +if efi_arch == 'aarch64' or efi_arch == 'arm' + # Aarch64 and ARM32 don't have an EFI capable objcopy. Use 'binary' + # instead, and add required symbols manually. + efi_ldflags += ['--defsym=EFI_SUBSYSTEM=0xa'] + efi_format = ['-O', 'binary'] +else + efi_format = ['--target=efi-app-@0@'.format(gnu_efi_arch)] +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_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_file1, o_file2, o_file3, o_file4], + output : 'fwup.so', + command : [efi_ld, '-o', '@OUTPUT@'] + + efi_ldflags + ['@INPUT@'] + + ['-lefi', '-lgnuefi', libgcc_file_name]) + +app = custom_target(efi_name, + input : so, + output : efi_name, + command : [objcopy, + '-j', '.text', + '-j', '.sdata', + '-j', '.data', + '-j', '.dynamic', + '-j', '.dynsym', + '-j', '.rel', + '-j', '.rela', + '-j', '.reloc'] + + efi_format + + ['@INPUT@', '@OUTPUT@'], + install : true, + install_dir : efi_app_location) + +dbg = custom_target('efi_debug', + input : so, + output : efi_name + '.debug', + command : [objcopy, + '-j', '.text', + '-j', '.sdata', + '-j', '.data', + '-j', '.dynamic', + '-j', '.dynsym', + '-j', '.rel*', + '-j', '.rela*', + '-j', '.reloc', + '-j', '.eh_frame', + '-j', '.debug*', + '-j', '.note.gnu.build-id'] + + efi_format + + ['@INPUT@', '@OUTPUT@'], + install : false, + install_dir : debugdir) diff -Nru fwupd-1.0.9/plugins/uefi/fu-plugin-uefi.c fwupd-1.2.10/plugins/uefi/fu-plugin-uefi.c --- fwupd-1.0.9/plugins/uefi/fu-plugin-uefi.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-plugin-uefi.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,56 +1,46 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes +/* + * Copyright (C) 2016-2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" -#include -#include #include #include +#include #include -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" -#ifndef HAVE_FWUP_GET_ESP_MOUNTPOINT -#define FWUP_SUPPORTED_STATUS_UNSUPPORTED 0 -#define FWUP_SUPPORTED_STATUS_UNLOCKED 1 -#define FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK 2 -#define FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK_NEXT_BOOT 3 -#define FWUPDATE_GUID EFI_GUID(0x0abba7dc,0xe516,0x4167,0xbbf5,0x4d,0x9d,0x1c,0x73,0x94,0x16) +#include "fu-uefi-bgrt.h" +#include "fu-uefi-common.h" +#include "fu-uefi-device.h" +#include "fu-uefi-vars.h" + +#ifndef HAVE_GIO_2_55_0 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUnixMountEntry, g_unix_mount_free) +#pragma clang diagnostic pop #endif struct FuPluginData { - gboolean ux_capsule; gchar *esp_path; - gint esrt_status; + gboolean require_shim_for_sb; + FuUefiBgrt *bgrt; }; -/* drop when upgrading minimum required version of efivar to 33 */ -#if !defined (efi_guid_ux_capsule) -#define efi_guid_ux_capsule EFI_GUID(0x3b8c8162,0x188c,0x46a4,0xaec9,0xbe,0x43,0xf1,0xd6,0x56,0x97) -#endif - void fu_plugin_init (FuPlugin *plugin) { FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); -#ifdef HAVE_FWUP_VERSION - g_autofree gchar *version_str = NULL; -#endif - data->ux_capsule = FALSE; - data->esp_path = NULL; + data->bgrt = fu_uefi_bgrt_new (); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); - fu_plugin_add_compile_version (plugin, "com.redhat.fwupdate", LIBFWUP_LIBRARY_VERSION); + 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); -#ifdef HAVE_FWUP_VERSION - version_str = g_strdup_printf ("%i", fwup_version ()); - fu_plugin_add_runtime_version (plugin, "com.redhat.fwupdate", version_str); -#endif + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } void @@ -58,163 +48,48 @@ { FuPluginData *data = fu_plugin_get_data (plugin); g_free (data->esp_path); + g_object_unref (data->bgrt); } -static gchar * -fu_plugin_uefi_guid_to_string (efi_guid_t *guid_raw) -{ - g_autofree gchar *guid = g_strdup ("00000000-0000-0000-0000-000000000000"); - if (efi_guid_to_str (guid_raw, &guid) < 0) - return NULL; - return g_steal_pointer (&guid); -} - -static fwup_resource * -fu_plugin_uefi_find_resource (fwup_resource_iter *iter, FuDevice *device, GError **error) -{ - efi_guid_t *guid_raw; - fwup_resource *re = NULL; - g_autofree gchar *guids_str = NULL; - - /* get the hardware we're referencing */ - while (fwup_resource_iter_next (iter, &re) > 0) { - g_autofree gchar *guid_tmp = NULL; - - /* convert to strings */ - fwup_get_guid (re, &guid_raw); - guid_tmp = fu_plugin_uefi_guid_to_string (guid_raw); - if (guid_tmp == NULL) { - g_warning ("failed to convert guid to string"); - continue; - } - - /* FIXME: also match hardware_instance too */ - if (fu_device_has_guid (device, guid_tmp)) - return re; - } - - /* paradoxically, no hardware matched */ - guids_str = fu_device_get_guids_as_str (device); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "No UEFI firmware matched '%s'", - guids_str); - return NULL; -} - -static void -_fwup_resource_iter_free (fwup_resource_iter *iter) -{ - fwup_resource_iter_destroy (&iter); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(fwup_resource_iter, _fwup_resource_iter_free); -#pragma clang diagnostic pop - gboolean fu_plugin_clear_results (FuPlugin *plugin, FuDevice *device, GError **error) { - fwup_resource *re = NULL; - g_autoptr(fwup_resource_iter) iter = NULL; - - /* get the hardware we're referencing */ - fwup_resource_iter_create (&iter); - re = fu_plugin_uefi_find_resource (iter, device, error); - if (re == NULL) - return FALSE; - if (fwup_clear_status (re) < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Cannot create clear UEFI status for %s", - fu_device_get_guid_default (device)); - return FALSE; - } - return TRUE; + FuUefiDevice *device_uefi = FU_UEFI_DEVICE (device); + return fu_uefi_device_clear_status (device_uefi, error); } gboolean fu_plugin_get_results (FuPlugin *plugin, FuDevice *device, GError **error) { + FuUefiDevice *device_uefi = FU_UEFI_DEVICE (device); + FuUefiDeviceStatus status = fu_uefi_device_get_status (device_uefi); const gchar *tmp; - fwup_resource *re = NULL; - guint32 status = 0; - guint32 version = 0; - time_t when = 0; - g_autoptr(fwup_resource_iter) iter = NULL; - - /* get the hardware we're referencing */ - fwup_resource_iter_create (&iter); - re = fu_plugin_uefi_find_resource (iter, device, error); - if (re == NULL) - return FALSE; - if (fwup_get_last_attempt_info (re, &version, &status, &when) < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Cannot get UEFI status for %s", - fu_device_get_guid_default (device)); - return FALSE; - } - if (status == FWUP_LAST_ATTEMPT_STATUS_SUCCESS) { + g_autofree gchar *err_msg = NULL; + g_autofree gchar *version_str = NULL; + + /* trivial case */ + if (status == FU_UEFI_DEVICE_STATUS_SUCCESS) { fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS); + return TRUE; + } + + /* something went wrong */ + if (status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_AC || + status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT) { + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); } else { - g_autofree gchar *err_msg = NULL; - g_autofree gchar *version_str = g_strdup_printf ("%u", version); fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); - tmp = fwup_last_attempt_status_to_string (status); - if (tmp == NULL) { - err_msg = g_strdup_printf ("failed to update to %s", - version_str); - } else { - err_msg = g_strdup_printf ("failed to update to %s: %s", - version_str, tmp); - } - fu_device_set_update_error (device, err_msg); } - return TRUE; -} - -static gboolean -fu_plugin_uefi_update_resource (fwup_resource *re, - guint64 hardware_instance, - GBytes *blob, - GError **error) -{ - int rc; - rc = fwup_set_up_update_with_buf (re, hardware_instance, - g_bytes_get_data (blob, NULL), - g_bytes_get_size (blob)); - if (rc < 0) { - g_autoptr(GString) str = g_string_new (NULL); - rc = 1; - for (int i = 0; rc > 0; i++) { - char *filename = NULL; - char *function = NULL; - char *message = NULL; - int line = 0; - int err = 0; - - rc = efi_error_get (i, &filename, &function, &line, - &message, &err); - if (rc <= 0) - break; - g_string_append_printf (str, "{error #%d} %s:%d %s(): %s: %s\t", - i, filename, line, function, - message, strerror (err)); - } - if (str->len > 1) - g_string_truncate (str, str->len - 1); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "UEFI firmware update failed: %s", - str->str); - return FALSE; + version_str = g_strdup_printf ("%u", fu_uefi_device_get_version_error (device_uefi)); + tmp = fu_uefi_device_status_to_string (status); + if (tmp == NULL) { + err_msg = g_strdup_printf ("failed to update to %s", + version_str); + } else { + err_msg = g_strdup_printf ("failed to update to %s: %s", + version_str, tmp); } + fu_device_set_update_error (device, err_msg); return TRUE; } @@ -289,13 +164,110 @@ 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_update_splash (GError **error) +fu_plugin_uefi_write_splash_data (FuPlugin *plugin, + FuDevice *device, + GBytes *blob, + GError **error) { - fwup_resource *re = NULL; + FuPluginData *data = fu_plugin_get_data (plugin); + guint32 screen_x, screen_y; + gsize buf_size = g_bytes_get_size (blob); + gssize size; + guint32 height, width; + 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, + .header_size = sizeof(efi_capsule_header_t), + .capsule_image_size = 0 + }; + g_autofree gchar *fn = NULL; + g_autofree gchar *directory = NULL; + g_autofree gchar *basename = NULL; + g_autoptr(GFile) ofile = NULL; + g_autoptr(GOutputStream) ostream = NULL; + + /* get screen dimensions */ + if (!fu_uefi_get_framebuffer_size (&screen_x, &screen_y, error)) + return FALSE; + if (!fu_uefi_get_bitmap_size ((const guint8 *) g_bytes_get_data (blob, NULL), + buf_size, &width, &height, error)) { + g_prefix_error (error, "splash invalid: "); + return FALSE; + } + + /* save to a predicatable filename */ + directory = fu_uefi_get_esp_path_for_os (data->esp_path); + basename = g_strdup_printf ("fwupd-%s.cap", FU_UEFI_VARS_GUID_UX_CAPSULE); + fn = g_build_filename (directory, "fw", basename, NULL); + if (!fu_common_mkdir_parent (fn, error)) + return FALSE; + ofile = g_file_new_for_path (fn); + ostream = G_OUTPUT_STREAM (g_file_replace (ofile, NULL, FALSE, + G_FILE_CREATE_NONE, NULL, error)); + if (ostream == NULL) + return FALSE; + + capsule_header.capsule_image_size = + g_bytes_get_size (blob) + + sizeof(efi_capsule_header_t) + + sizeof(efi_ux_capsule_header_t); + + header.version = 1; + header.image_type = 0; + header.reserved = 0; + header.x_offset = (screen_x / 2) - (width / 2); + 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); + if (size < 0) + return FALSE; + size = g_output_stream_write (ostream, &header, sizeof(header), NULL, error); + if (size < 0) + return FALSE; + size = g_output_stream_write_bytes (ostream, blob, NULL, error); + if (size < 0) + return FALSE; + + /* write display capsule location as UPDATE_INFO */ + if (!fu_uefi_device_write_update_info (FU_UEFI_DEVICE (device), fn, + "fwupd-ux-capsule", + &efi_guid_ux_capsule, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_plugin_uefi_update_splash (FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); guint best_idx = G_MAXUINT; guint32 lowest_border_pixels = G_MAXUINT; - int rc; guint32 screen_height = 768; guint32 screen_width = 1024; g_autoptr(GBytes) image_bmp = NULL; @@ -315,16 +287,24 @@ { 0, 0 } }; + /* no UX capsule support, so deleting var if it exists */ + if (fu_device_has_custom_flag (device, "no-ux-capsule")) { + g_debug ("not providing UX capsule"); + return fu_uefi_vars_delete (FU_UEFI_VARS_GUID_FWUPDATE, + "fwupd-ux-capsule", error); + } + /* get the boot graphics resource table data */ - rc = fwup_get_ux_capsule_info (&screen_width, &screen_height); - if (rc < 0) { + if (!fu_uefi_bgrt_get_supported (data->bgrt)) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "failed to get BGRT screen size"); + "BGRT is not supported"); return FALSE; } - g_debug ("BGRT screen size %" G_GUINT32_FORMAT " x%" G_GUINT32_FORMAT, + if (!fu_uefi_get_framebuffer_size (&screen_width, &screen_height, error)) + return FALSE; + g_debug ("framebuffer size %" G_GUINT32_FORMAT " x%" G_GUINT32_FORMAT, screen_width, screen_height); /* find the 'best sized' pre-generated image */ @@ -361,7 +341,7 @@ return FALSE; /* perform the upload */ - return fu_plugin_uefi_update_resource (re, 0, image_bmp, error); + return fu_plugin_uefi_write_splash_data (plugin, device, image_bmp, error); } static gboolean @@ -395,46 +375,58 @@ FwupdInstallFlags flags, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - fwup_resource *re = NULL; - guint64 hardware_instance = 0; /* FIXME */ - g_autoptr(fwup_resource_iter) iter = NULL; const gchar *str; + guint32 flashes_left; g_autofree gchar *efibootmgr_path = NULL; g_autofree gchar *boot_variables = NULL; g_autoptr(GError) error_splash = NULL; - /* get the hardware we're referencing */ - fwup_resource_iter_create (&iter); - re = fu_plugin_uefi_find_resource (iter, device, error); - if (re == NULL) - return FALSE; + /* test the flash counter */ + flashes_left = fu_device_get_flashes_left (device); + if (flashes_left > 0) { + g_debug ("%s has %" G_GUINT32_FORMAT " flashes left", + fu_device_get_name (device), + flashes_left); + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && flashes_left <= 2) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s only has %" G_GUINT32_FORMAT " flashes left -- " + "see https://github.com/hughsie/fwupd/wiki/Dell-TPM:-flashes-left for more information.", + fu_device_get_name (device), flashes_left); + return FALSE; + } + } /* TRANSLATORS: this is shown when updating the firmware after the reboot */ str = _("Installing firmware update…"); g_assert (str != NULL); /* make sure that the ESP is mounted */ - if (!fu_plugin_uefi_esp_mounted (plugin, error)) - return FALSE; + if (g_getenv ("FWUPD_UEFI_ESP_PATH") == NULL) { + if (!fu_plugin_uefi_esp_mounted (plugin, error)) + return FALSE; + } /* perform the update */ g_debug ("Performing UEFI capsule update"); fu_device_set_status (device, FWUPD_STATUS_SCHEDULING); - - if (data->ux_capsule) { - if (!fu_plugin_uefi_update_splash (&error_splash)) { - g_warning ("failed to upload UEFI UX capsule text: %s", - error_splash->message); - } + if (!fu_plugin_uefi_update_splash (plugin, device, &error_splash)) { + g_debug ("failed to upload UEFI UX capsule text: %s", + error_splash->message); } - if (!fu_plugin_uefi_update_resource (re, hardware_instance, blob_fw, error)) + if (!fu_device_write_firmware (device, blob_fw, flags, 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 = g_find_program_in_path ("efibootmgr"); + efibootmgr_path = fu_common_find_program_in_path ("efibootmgr", NULL); if (efibootmgr_path != NULL) { - if (!g_spawn_command_line_sync ("efibootmgr -v", + g_autofree gchar *cmd = g_strdup_printf ("%s -v", efibootmgr_path); + if (!g_spawn_command_line_sync (cmd, &boot_variables, NULL, NULL, error)) return FALSE; g_message ("Boot Information:\n%s", boot_variables); @@ -443,78 +435,77 @@ return TRUE; } -static AsVersionParseFlag -fu_plugin_uefi_get_version_format_for_type (FuPlugin *plugin, guint32 uefi_type) +static void +fu_plugin_uefi_register_proxy_device (FuPlugin *plugin, FuDevice *device) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_dev (device); + if (data->esp_path != NULL) + fu_device_set_metadata (FU_DEVICE (dev), "EspPath", data->esp_path); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); +} + +void +fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) +{ + if (fu_device_get_metadata (device, "UefiDeviceKind") != NULL) { + if (fu_device_get_guid_default (device) == NULL) { + g_autofree gchar *dbg = fu_device_to_string (device); + g_warning ("cannot create proxy device as no GUID: %s", dbg); + return; + } + fu_plugin_uefi_register_proxy_device (plugin, device); + } +} + +static FwupdVersionFormat +fu_plugin_uefi_get_version_format_for_type (FuPlugin *plugin, FuUefiDeviceKind device_kind) { const gchar *content; const gchar *quirk; + g_autofree gchar *group = NULL; /* we have no information for devices */ - if (uefi_type == FWUP_RESOURCE_TYPE_DEVICE_FIRMWARE) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) + return FWUPD_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 FWUPD_VERSION_FORMAT_TRIPLET; /* any quirks match */ - quirk = fu_plugin_lookup_quirk_by_id (plugin, - FU_QUIRKS_UEFI_VERSION_FORMAT, - content); - if (g_strcmp0 (quirk, "none") == 0) - return AS_VERSION_PARSE_FLAG_NONE; - - /* fall back */ - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; -} - -gboolean -fu_plugin_unlock (FuPlugin *plugin, - FuDevice *device, - GError **error) -{ - gint rc; - g_debug ("unlocking UEFI device %s", fu_device_get_id (device)); - rc = fwup_enable_esrt(); - if (rc <= 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to unlock UEFI device"); - return FALSE; - } else if (rc == 1) - g_debug ("UEFI device is already unlocked"); - else if (rc == 2) - g_debug ("Successfully unlocked UEFI device"); - else if (rc == 3) - g_debug ("UEFI device will be unlocked on next reboot"); - return TRUE; + group = g_strdup_printf ("SmbiosManufacturer=%s", content); + quirk = fu_plugin_lookup_quirk_by_id (plugin, group, + FU_QUIRKS_UEFI_VERSION_FORMAT); + if (quirk == NULL) + return FWUPD_VERSION_FORMAT_TRIPLET; + return fwupd_version_format_from_string (quirk); } static const gchar * -fu_plugin_uefi_uefi_type_to_string (guint32 uefi_type) +fu_plugin_uefi_uefi_type_to_string (FuUefiDeviceKind device_kind) { - if (uefi_type == FWUP_RESOURCE_TYPE_UNKNOWN) + if (device_kind == FU_UEFI_DEVICE_KIND_UNKNOWN) return "Unknown Firmware"; - if (uefi_type == FWUP_RESOURCE_TYPE_SYSTEM_FIRMWARE) + if (device_kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) return "System Firmware"; - if (uefi_type == FWUP_RESOURCE_TYPE_DEVICE_FIRMWARE) + if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) return "Device Firmware"; - if (uefi_type == FWUP_RESOURCE_TYPE_UEFI_DRIVER) + if (device_kind == FU_UEFI_DEVICE_KIND_UEFI_DRIVER) return "UEFI Driver"; - if (uefi_type == FWUP_RESOURCE_TYPE_FMP) + if (device_kind == FU_UEFI_DEVICE_KIND_FMP) return "Firmware Management Protocol"; return NULL; } static gchar * -fu_plugin_uefi_get_name_for_type (FuPlugin *plugin, guint32 uefi_type) +fu_plugin_uefi_get_name_for_type (FuPlugin *plugin, FuUefiDeviceKind device_kind) { GString *display_name; /* set Display Name prefix for capsules that are not PCI cards */ - display_name = g_string_new (fu_plugin_uefi_uefi_type_to_string (uefi_type)); - if (uefi_type == FWUP_RESOURCE_TYPE_DEVICE_FIRMWARE) { + display_name = g_string_new (fu_plugin_uefi_uefi_type_to_string (device_kind)); + if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) { g_string_prepend (display_name, "UEFI "); } else { const gchar *tmp; @@ -527,122 +518,53 @@ return g_string_free (display_name, FALSE); } -static void -fu_plugin_uefi_coldplug_resource (FuPlugin *plugin, fwup_resource *re) +static gboolean +fu_plugin_uefi_coldplug_device (FuPlugin *plugin, FuUefiDevice *dev, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - AsVersionParseFlag parse_flags; - efi_guid_t *guid_raw; - guint32 uefi_type; - guint32 version_raw; - guint64 hardware_instance = 0; /* FIXME */ - g_autofree gchar *guid = NULL; - g_autofree gchar *id = NULL; - g_autofree gchar *name = NULL; - g_autofree gchar *version_lowest = NULL; - g_autofree gchar *version = NULL; - g_autoptr(FuDevice) dev = NULL; - - /* detect the fake GUID used for uploading the image */ - fwup_get_guid (re, &guid_raw); - if (efi_guid_cmp (guid_raw, &efi_guid_ux_capsule) == 0) { - data->ux_capsule = TRUE; - return; - } - - /* convert to strings */ - guid = fu_plugin_uefi_guid_to_string (guid_raw); - if (guid == NULL) { - g_warning ("failed to convert guid to string"); - return; - } - - fwup_get_fw_type (re, &uefi_type); - parse_flags = fu_plugin_uefi_get_version_format_for_type (plugin, uefi_type); - fwup_get_fw_version (re, &version_raw); - version = as_utils_version_from_uint32 (version_raw, parse_flags); - id = g_strdup_printf ("UEFI-%s-dev%" G_GUINT64_FORMAT, - guid, hardware_instance); - - dev = fu_device_new (); - fu_device_set_id (dev, id); - fu_device_add_guid (dev, guid); - fu_device_set_version (dev, version); - name = fu_plugin_uefi_get_name_for_type (plugin, uefi_type); - if (name != NULL) - fu_device_set_name (dev, name); - fwup_get_lowest_supported_fw_version (re, &version_raw); - if (version_raw != 0) { - version_lowest = as_utils_version_from_uint32 (version_raw, - parse_flags); - fu_device_set_version_lowest (dev, version_lowest); - } - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); - if (g_file_test ("/sys/firmware/efi/efivars", G_FILE_TEST_IS_DIR) || - g_file_test ("/sys/firmware/efi/vars", G_FILE_TEST_IS_DIR)) { - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - } else { - g_warning ("Kernel support for EFI variables missing"); + FuUefiDeviceKind device_kind; + FwupdVersionFormat version_format; + + /* set default version format */ + device_kind = fu_uefi_device_get_kind (dev); + 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; + + /* if not already set by quirks */ + if (fu_device_get_custom_flags (FU_DEVICE (dev)) == NULL) { + /* 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 (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); - if (uefi_type == FWUP_RESOURCE_TYPE_DEVICE_FIRMWARE) { - /* nothing better in the icon naming spec */ - fu_device_add_icon (dev, "audio-card"); - } else { - /* this is probably system firmware */ - fu_device_add_icon (dev, "computer"); - fu_device_add_guid (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); } - fu_plugin_device_add (plugin, dev); + + /* success */ + return TRUE; } static void fu_plugin_uefi_test_secure_boot (FuPlugin *plugin) { - const efi_guid_t guid = EFI_GLOBAL_GUID; const gchar *result_str = "Disabled"; - g_autofree guint8 *data = NULL; - gsize data_size = 0; - guint32 attributes = 0; - gint rc; - - rc = efi_get_variable (guid, "SecureBoot", &data, &data_size, &attributes); - if (rc < 0) - return; - if (data_size >= 1 && data[0] & 1) + if (fu_uefi_secure_boot_enabled ()) result_str = "Enabled"; - g_debug ("SecureBoot is: %s", result_str); fu_plugin_add_report_metadata (plugin, "SecureBoot", result_str); } static gboolean -fu_plugin_uefi_set_custom_mountpoint (FuPlugin *plugin, GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - const gchar *key = "OverrideESPMountPoint"; - - /* load from file and keep @key ref'd for the lifetime of the plugin as - * libfwupdate does not strdup the value in fwup_set_esp_mountpoint() */ - data->esp_path = fu_plugin_get_config_value (plugin, key); - if (data->esp_path != NULL) { - if (!g_file_test (data->esp_path, G_FILE_TEST_IS_DIR)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Invalid %s specified in %s config: %s", - fu_plugin_get_name (plugin), key, - data->esp_path); - - return FALSE; - } - fwup_set_esp_mountpoint (data->esp_path); - } - return TRUE; -} - -static gboolean fu_plugin_uefi_delete_old_capsules (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); @@ -653,7 +575,7 @@ files = fu_common_get_files_recursive (data->esp_path, error); if (files == NULL) return FALSE; - pattern = g_build_filename (data->esp_path, "EFI/*/fw/fwupdate-*.cap", NULL); + pattern = g_build_filename (data->esp_path, "EFI/*/fw/fwupd-*.cap", NULL); for (guint i = 0; i < files->len; i++) { const gchar *fn = g_ptr_array_index (files, i); if (fnmatch (pattern, fn, 0) == 0) { @@ -666,138 +588,321 @@ return TRUE; } -static gboolean -fu_plugin_uefi_delete_old_efivars (FuPlugin *plugin, GError **error) +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) { - char *name = NULL; - efi_guid_t fwupdate_guid = FWUPDATE_GUID; - efi_guid_t *guid = NULL; - int rc; - while ((rc = efi_get_next_variable_name (&guid, &name)) > 0) { - if (efi_guid_cmp (guid, &fwupdate_guid) != 0) - continue; - if (g_str_has_prefix (name, "fwupdate-")) { - g_debug ("deleting %s", name); - rc = efi_del_variable (fwupdate_guid, name); - if (rc < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to delete efi var %s: %s", - name, strerror (errno)); - return FALSE; - } - } + const guint8 *data; + gsize sz; + g_autoptr(GBytes) bios_information = fu_plugin_get_smbios_data (plugin, 0); + if (bios_information == NULL) { + const gchar *tmp = g_getenv ("FWUPD_DELL_FAKE_SMBIOS"); + if (tmp != NULL) + return TRUE; + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "SMBIOS not supported"); + return FALSE; } - if (rc < 0) { + data = g_bytes_get_data (bios_information, &sz); + if (sz < 0x13) { g_set_error (error, FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "error listing variables: %s", - strerror (errno)); + FWUPD_ERROR_INVALID_FILE, + "offset bigger than size %" G_GSIZE_FORMAT, sz); return FALSE; } + if (data[1] < 0x13) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "SMBIOS 2.3 not supported"); + return FALSE; + } + if (!(data[0x13] & (1 << 3))) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "System does not support UEFI mode"); + return FALSE; + } + /* test for invalid ESP in coldplug, and set the update-error rather + * than showing no output if the plugin had self-disabled here */ return TRUE; } -/* remove when bumping minimum efivar to 35 */ -static int -_efi_get_variable_exists (efi_guid_t guid, const char *name) +static gboolean +fu_plugin_uefi_ensure_esp_path (FuPlugin *plugin, GError **error) { - uint32_t unused_attrs = 0; - return efi_get_variable_attributes (guid, name, &unused_attrs); + FuPluginData *data = fu_plugin_get_data (plugin); + guint64 sz_reqd = FU_UEFI_COMMON_REQUIRED_ESP_FREE_SPACE; + g_autofree gchar *require_esp_free_space = NULL; + g_autofree gchar *require_shim_for_sb = NULL; + + /* parse free space */ + require_esp_free_space = fu_plugin_get_config_value (plugin, "RequireESPFreeSpace"); + if (require_esp_free_space != NULL) + sz_reqd = fu_common_strtoull (require_esp_free_space); + + /* load from file */ + data->esp_path = fu_plugin_get_config_value (plugin, "OverrideESPMountPoint"); + if (data->esp_path != NULL) { + g_autoptr(GError) error_local = NULL; + if (!fu_uefi_check_esp_path (data->esp_path, &error_local)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_FILENAME, + "invalid OverrideESPMountPoint=%s specified in config: %s", + data->esp_path, error_local->message); + return FALSE; + } + return fu_uefi_check_esp_free_space (data->esp_path, sz_reqd, error); + } + require_shim_for_sb = fu_plugin_get_config_value (plugin, "RequireShimForSecureBoot"); + if (require_shim_for_sb == NULL || + g_ascii_strcasecmp (require_shim_for_sb, "true") == 0) + data->require_shim_for_sb = TRUE; + + /* try to guess from heuristics */ + data->esp_path = fu_uefi_guess_esp_path (); + if (data->esp_path == NULL) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_FILENAME, + "Unable to determine EFI system partition location, " + "See https://github.com/hughsie/fwupd/wiki/Determining-EFI-system-partition-location"); + return FALSE; + } + + /* check free space */ + if (!fu_uefi_check_esp_free_space (data->esp_path, sz_reqd, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_plugin_uefi_ensure_efivarfs_rw (GError **error) +{ + g_autofree gchar *sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + g_autofree gchar *sysfsefivardir = g_build_filename (sysfsfwdir, "efi", "efivars", NULL); + g_autoptr(GUnixMountEntry) mount = g_unix_mount_at (sysfsefivardir, NULL); + + if (mount == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "%s was not mounted", sysfsefivardir); + return FALSE; + } + if (g_unix_mount_is_readonly (mount)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is read only", sysfsefivardir); + return FALSE; + } + + return TRUE; } gboolean -fu_plugin_startup (FuPlugin *plugin, GError **error) +fu_plugin_unlock (FuPlugin *plugin, FuDevice *device, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); + FuUefiDevice *device_uefi = FU_UEFI_DEVICE (device); + FuDevice *device_alt = NULL; + FwupdDeviceFlags device_flags_alt = 0; + guint flashes_left = 0; + guint flashes_left_alt = 0; - /* get the supported status */ - data->esrt_status = fwup_supported (); - if (data->esrt_status == FWUP_SUPPORTED_STATUS_UNSUPPORTED) { - g_set_error_literal (error, + if (fu_uefi_device_get_kind (device_uefi) != + FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unable to unlock %s", + fu_device_get_name (device)); + return FALSE; + } + + /* for unlocking TPM1.2 <-> TPM2.0 switching */ + g_debug ("Unlocking upgrades for: %s (%s)", fu_device_get_name (device), + fu_device_get_id (device)); + device_alt = fu_device_get_alternate (device); + if (device_alt == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No alternate device for %s", + fu_device_get_name (device)); + return FALSE; + } + g_debug ("Preventing upgrades for: %s (%s)", fu_device_get_name (device_alt), + fu_device_get_id (device_alt)); + + flashes_left = fu_device_get_flashes_left (device); + flashes_left_alt = fu_device_get_flashes_left (device_alt); + if (flashes_left == 0) { + /* flashes left == 0 on both means no flashes left */ + if (flashes_left_alt == 0) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "UEFI firmware updating not supported"); + "ERROR: %s has no flashes left.", + fu_device_get_name (device)); + /* flashes left == 0 on just unlocking device is ownership */ + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ERROR: %s is currently OWNED. " + "Ownership must be removed to switch modes.", + fu_device_get_name (device_alt)); + } return FALSE; } - /* load any overriden options */ - if (!fu_plugin_uefi_set_custom_mountpoint (plugin, error)) + /* clone the info from real device but prevent it from being flashed */ + device_flags_alt = fu_device_get_flags (device_alt); + fu_device_set_flags (device, device_flags_alt); + fu_device_set_flags (device_alt, device_flags_alt & ~FWUPD_DEVICE_FLAG_UPDATABLE); + + /* make sure that this unlocked device can be updated */ + fu_device_set_version (device, "0.0.0.0", FWUPD_VERSION_FORMAT_QUAD); + return TRUE; +} + +static gboolean +fu_plugin_uefi_create_dummy (FuPlugin *plugin, GError **error) +{ + const gchar *key; + g_autoptr(FuDevice) dev = fu_device_new (); + + key = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); + if (key != NULL) + fu_device_set_vendor (dev, key); + key = fu_plugin_uefi_get_name_for_type (plugin, FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE); + fu_device_set_name (dev, key); + key = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VERSION); + if (key != NULL) + fu_device_set_version (dev, key, FWUPD_VERSION_FORMAT_PLAIN); + key = "Firmware can not be updated in legacy mode, switch to UEFI mode."; + fu_device_set_update_error (dev, key); + + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); + + fu_device_add_icon (dev, "computer"); + fu_device_set_plugin (dev, fu_plugin_get_name (plugin)); + fu_device_set_id (dev, "UEFI-dummy"); + fu_device_add_instance_id (dev, "main-system-firmware"); + if (!fu_device_setup (dev, error)) return FALSE; + fu_plugin_device_add (plugin, dev); - /* get the default compiled-in value for the ESP mountpoint */ -#ifdef HAVE_FWUP_GET_ESP_MOUNTPOINT - if (data->esp_path == NULL) - data->esp_path = g_strdup (fwup_get_esp_mountpoint ()); -#endif + return TRUE; +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + const gchar *str; + g_autofree gchar *bootloader = NULL; + g_autofree gchar *esrt_path = NULL; + g_autofree gchar *sysfsfwdir = NULL; + g_autoptr(GError) error_bootloader = NULL; + g_autoptr(GError) error_efivarfs = NULL; + g_autoptr(GError) error_esp = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) entries = NULL; + + /* are the EFI dirs set up so we can update each device */ + if (!fu_uefi_vars_supported (&error_local)) { + g_warning ("%s", error_local->message); + return fu_plugin_uefi_create_dummy (plugin, error); + } + + /* get the directory of ESRT entries */ + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); + entries = fu_uefi_get_esrt_entry_paths (esrt_path, error); + if (entries == NULL) + return FALSE; + + /* make sure that efivarfs is rw */ + if (!fu_plugin_uefi_ensure_efivarfs_rw (&error_efivarfs)) + g_warning ("%s", error_efivarfs->message); + + /* if secure boot is enabled ensure we have a signed fwupd.efi */ + bootloader = fu_uefi_get_built_app_path (&error_bootloader); + if (bootloader == NULL) { + if (fu_uefi_secure_boot_enabled ()) + g_prefix_error (&error_bootloader, "missing signed bootloader for secure boot: "); + g_warning ("%s", error_bootloader->message); + } + + /* ensure the ESP is detected */ + if (!fu_plugin_uefi_ensure_esp_path (plugin, &error_esp)) + g_warning ("%s", error_esp->message); - /* fall back to a sane default */ - if (data->esp_path == NULL) - data->esp_path = g_strdup ("/boot/efi"); + /* add each device */ + for (guint i = 0; i < entries->len; i++) { + const gchar *path = g_ptr_array_index (entries, i); + 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) { + fu_device_set_update_error (FU_DEVICE (dev), error_esp->message); + } else if (error_bootloader != NULL) { + fu_device_set_update_error (FU_DEVICE (dev), error_bootloader->message); + } else if (error_efivarfs != NULL) { + fu_device_set_update_error (FU_DEVICE (dev), error_efivarfs->message); + } else { + fu_device_set_metadata (FU_DEVICE (dev), "EspPath", data->esp_path); + fu_device_set_metadata_boolean (FU_DEVICE (dev), + "RequireShimForSecureBoot", + data->require_shim_for_sb); + fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_UPDATABLE); + } + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + } + + /* no devices are updatable */ + if (error_esp != NULL || error_bootloader != NULL) + return TRUE; /* delete any existing .cap files to avoid the small ESP partition * from running out of space when we've done lots of firmware updates * -- also if the distro has changed the ESP may be different anyway */ - if (_efi_get_variable_exists (EFI_GLOBAL_GUID, "BootNext") == 0) { + if (fu_uefi_vars_exists (FU_UEFI_VARS_GUID_EFI_GLOBAL, "BootNext")) { g_debug ("detected BootNext, not cleaning up"); } else { if (!fu_plugin_uefi_delete_old_capsules (plugin, error)) return FALSE; - if (!fu_plugin_uefi_delete_old_efivars (plugin, error)) + if (!fu_uefi_vars_delete_with_glob (FU_UEFI_VARS_GUID_FWUPDATE, "fwupd-*", error)) return FALSE; } /* save in report metadata */ g_debug ("ESP mountpoint set as %s", data->esp_path); fu_plugin_add_report_metadata (plugin, "ESPMountPoint", data->esp_path); - return TRUE; -} - -gboolean -fu_plugin_coldplug (FuPlugin *plugin, GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - fwup_resource *re; - g_autoptr(fwup_resource_iter) iter = NULL; - g_autofree gchar *name = NULL; - const gchar *ux_capsule_str = "Disabled"; - - /* create a dummy device so we can unlock the feature */ - if (data->esrt_status == FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK) { - g_autoptr(FuDevice) dev = fu_device_new (); - name = fu_plugin_uefi_get_name_for_type (plugin, - FWUP_RESOURCE_TYPE_SYSTEM_FIRMWARE); - if (name != NULL) - fu_device_set_name (dev, name); - fu_device_set_id (dev, "UEFI-dummy-dev0"); - fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); - fu_device_set_version (dev, "0"); - fu_device_add_icon (dev, "computer"); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_LOCKED); - fu_plugin_device_add (plugin, dev); - return TRUE; - } - - /* add each device */ - if (fwup_resource_iter_create (&iter) < 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Cannot create fwup iter"); - return FALSE; - } - while (fwup_resource_iter_next (iter, &re) > 0) - fu_plugin_uefi_coldplug_resource (plugin, re); /* for debugging problems later */ fu_plugin_uefi_test_secure_boot (plugin); - if (data->ux_capsule) - ux_capsule_str = "Enabled"; - g_debug ("UX Capsule support : %s", ux_capsule_str); - fu_plugin_add_report_metadata (plugin, "UEFIUXCapsule", ux_capsule_str); + if (!fu_uefi_bgrt_setup (data->bgrt, &error_local)) + g_debug ("BGRT setup failed: %s", error_local->message); + str = fu_uefi_bgrt_get_supported (data->bgrt) ? "Enabled" : "Disabled"; + g_debug ("UX Capsule support : %s", str); + fu_plugin_add_report_metadata (plugin, "UEFIUXCapsule", str); return TRUE; } diff -Nru fwupd-1.0.9/plugins/uefi/fu-self-test.c fwupd-1.2.10/plugins/uefi/fu-self-test.c --- fwupd-1.0.9/plugins/uefi/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-test.h" +#include "fu-ucs2.h" +#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) +{ + g_autofree guint16 *str1 = NULL; + g_autofree gchar *str2 = NULL; + str1 = fu_uft8_to_ucs2 ("hw!", -1); + g_assert_cmpint (fu_ucs2_strlen (str1, -1), ==, 3); + str2 = fu_ucs2_to_uft8 (str1, -1); + g_assert_cmpstr ("hw!", ==, str2); +} + +static void +fu_uefi_bgrt_func (void) +{ + gboolean ret; + g_autoptr(GError) error = NULL; + g_autoptr(FuUefiBgrt) bgrt = fu_uefi_bgrt_new (); + ret = fu_uefi_bgrt_setup (bgrt, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_true (fu_uefi_bgrt_get_supported (bgrt)); + g_assert_cmpint (fu_uefi_bgrt_get_xoffset (bgrt), ==, 123); + g_assert_cmpint (fu_uefi_bgrt_get_yoffset (bgrt), ==, 456); + g_assert_cmpint (fu_uefi_bgrt_get_width (bgrt), ==, 54); + g_assert_cmpint (fu_uefi_bgrt_get_height (bgrt), ==, 24); +} + +static void +fu_uefi_framebuffer_func (void) +{ + gboolean ret; + guint32 height = 0; + guint32 width = 0; + g_autoptr(GError) error = NULL; + ret = fu_uefi_get_framebuffer_size (&width, &height, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_cmpint (width, ==, 456); + g_assert_cmpint (height, ==, 789); +} + +static void +fu_uefi_bitmap_func (void) +{ + gboolean ret; + gsize sz = 0; + guint32 height = 0; + guint32 width = 0; + g_autofree gchar *fn = NULL; + g_autofree gchar *buf = NULL; + g_autoptr(GError) error = NULL; + + fn = fu_test_get_filename (TESTDATADIR, "test.bmp"); + g_assert (fn != NULL); + ret = g_file_get_contents (fn, &buf, &sz, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_nonnull (buf); + ret = fu_uefi_get_bitmap_size ((guint8 *)buf, sz, &width, &height, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_cmpint (width, ==, 54); + g_assert_cmpint (height, ==, 24); +} + +static void +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, &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"); + g_assert_cmpint (fu_uefi_device_get_hardware_instance (dev), ==, 0x0); + g_assert_cmpint (fu_uefi_device_get_version (dev), ==, 65586); + g_assert_cmpint (fu_uefi_device_get_version_lowest (dev), ==, 65582); + g_assert_cmpint (fu_uefi_device_get_version_error (dev), ==, 18472960); + g_assert_cmpint (fu_uefi_device_get_capsule_flags (dev), ==, 0xfe); + g_assert_cmpint (fu_uefi_device_get_status (dev), ==, FU_UEFI_DEVICE_STATUS_ERROR_UNSUCCESSFUL); + + /* check enums all converted */ + for (guint i = 0; i < FU_UEFI_DEVICE_STATUS_LAST; i++) + g_assert_nonnull (fu_uefi_device_status_to_string (i)); +} + +static void +fu_uefi_vars_func (void) +{ + gboolean ret; + gsize sz = 0; + guint32 attr = 0; + g_autofree guint8 *data = NULL; + g_autoptr(GError) error = NULL; + + /* check supported */ + ret = fu_uefi_vars_supported (&error); + g_assert_no_error (error); + g_assert_true (ret); + + /* check existing keys */ + g_assert_false (fu_uefi_vars_exists (FU_UEFI_VARS_GUID_EFI_GLOBAL, "NotGoingToExist")); + g_assert_true (fu_uefi_vars_exists (FU_UEFI_VARS_GUID_EFI_GLOBAL, "SecureBoot")); + + /* write and read a key */ + ret = fu_uefi_vars_set_data (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test", + (guint8 *) "1", 1, + FU_UEFI_VARS_ATTR_NON_VOLATILE | + FU_UEFI_VARS_ATTR_RUNTIME_ACCESS, + &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_uefi_vars_get_data (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test", + &data, &sz, &attr, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_cmpint (sz, ==, 1); + g_assert_cmpint (attr, ==, FU_UEFI_VARS_ATTR_NON_VOLATILE | + FU_UEFI_VARS_ATTR_RUNTIME_ACCESS); + g_assert_cmpint (data[0], ==, '1'); + + /* delete single key */ + ret = fu_uefi_vars_delete (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test", &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_false (fu_uefi_vars_exists (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test")); + + /* delete multiple keys */ + ret = fu_uefi_vars_set_data (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test1", (guint8 *)"1", 1, 0, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_uefi_vars_set_data (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test2", (guint8 *)"1", 1, 0, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_uefi_vars_delete_with_glob (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test*", &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_false (fu_uefi_vars_exists (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test1")); + g_assert_false (fu_uefi_vars_exists (FU_UEFI_VARS_GUID_EFI_GLOBAL, "Test2")); + + /* read a key that doesn't exist */ + ret = fu_uefi_vars_get_data (FU_UEFI_VARS_GUID_EFI_GLOBAL, "NotGoingToExist", NULL, NULL, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert_false (ret); +} + +static void +fu_uefi_plugin_func (void) +{ + FuUefiDevice *dev; + g_autofree gchar *esrt_path = NULL; + g_autofree gchar *sysfsfwdir = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) entries = NULL; + + /* add each device */ + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); + entries = fu_uefi_get_esrt_entry_paths (esrt_path, &error); + g_assert_no_error (error); + g_assert_nonnull (entries); + 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(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); + + /* system firmware */ + dev = g_ptr_array_index (devices, 0); + 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"); + g_assert_cmpint (fu_uefi_device_get_version (dev), ==, 65586); + g_assert_cmpint (fu_uefi_device_get_version_lowest (dev), ==, 65582); + g_assert_cmpint (fu_uefi_device_get_version_error (dev), ==, 18472960); + g_assert_cmpint (fu_uefi_device_get_capsule_flags (dev), ==, 0xfe); + g_assert_cmpint (fu_uefi_device_get_status (dev), ==, FU_UEFI_DEVICE_STATUS_ERROR_UNSUCCESSFUL); + + /* system firmware */ + dev = g_ptr_array_index (devices, 1); + g_assert_cmpint (fu_uefi_device_get_kind (dev), ==, FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE); + g_assert_cmpstr (fu_uefi_device_get_guid (dev), ==, "671d19d0-d43c-4852-98d9-1ce16f9967e4"); + g_assert_cmpint (fu_uefi_device_get_version (dev), ==, 3090287969); + g_assert_cmpint (fu_uefi_device_get_version_lowest (dev), ==, 1); + g_assert_cmpint (fu_uefi_device_get_version_error (dev), ==, 0); + g_assert_cmpint (fu_uefi_device_get_capsule_flags (dev), ==, 32784); + g_assert_cmpint (fu_uefi_device_get_status (dev), ==, FU_UEFI_DEVICE_STATUS_SUCCESS); +} + +static void +fu_uefi_update_info_func (void) +{ + g_autofree gchar *fn = NULL; + g_autoptr(FuUefiDevice) dev = NULL; + g_autoptr(FuUefiUpdateInfo) info = 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, &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"); + info = fu_uefi_device_load_update_info (dev, &error); + g_assert_no_error (error); + g_assert_nonnull (info); + g_assert_cmpint (fu_uefi_update_info_get_version (info), ==, 0x7); + g_assert_cmpstr (fu_uefi_update_info_get_guid (info), ==, "697bd920-12cf-4da9-8385-996909bc6559"); + g_assert_cmpint (fu_uefi_update_info_get_capsule_flags (info), ==, 0x50000); + g_assert_cmpint (fu_uefi_update_info_get_hw_inst (info), ==, 0x0); + g_assert_cmpint (fu_uefi_update_info_get_status (info), ==, FU_UEFI_UPDATE_INFO_STATUS_ATTEMPT_UPDATE); + g_assert_cmpstr (fu_uefi_update_info_get_capsule_fn (info), ==, + "/EFI/fedora/fw/fwupd-697bd920-12cf-4da9-8385-996909bc6559.cap"); +} + +int +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); + g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); + + /* 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); + g_test_add_func ("/uefi/framebuffer", fu_uefi_framebuffer_func); + g_test_add_func ("/uefi/bitmap", fu_uefi_bitmap_func); + g_test_add_func ("/uefi/device", fu_uefi_device_func); + g_test_add_func ("/uefi/update-info", fu_uefi_update_info_func); + g_test_add_func ("/uefi/plugin", fu_uefi_plugin_func); + return g_test_run (); +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-ucs2.c fwupd-1.2.10/plugins/uefi/fu-ucs2.c --- fwupd-1.0.9/plugins/uefi/fu-ucs2.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-ucs2.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ucs2.h" + +#define ev_bits(val, mask, shift) (((val) & ((mask) << (shift))) >> (shift)) + +gchar * +fu_ucs2_to_uft8 (const guint16 *str, gssize max) +{ + gssize i, j; + gchar *ret; + + if (max < 0) + max = fu_ucs2_strlen (str, max); + ret = g_malloc0 (max * 3 + 1); /* would be s/3/6 if this were UCS-4 */ + for (i = 0, j = 0; i < max && str[i]; i++, j++) { + if (str[i] <= 0x7f) { + ret[j] = str[i]; + } else if (str[i] > 0x7f && str[i] <= 0x7ff) { + ret[j++] = 0xc0 | ev_bits(str[i], 0x1f, 6); + ret[j] = 0x80 | ev_bits(str[i], 0x3f, 0); + } else if (str[i] > 0x7ff /* && str[i] < 0x10000 */ ) { + ret[j++] = 0xe0 | ev_bits(str[i], 0xf, 12); + ret[j++] = 0x80 | ev_bits(str[i], 0x3f, 6); + ret[j] = 0x80 | ev_bits(str[i], 0x3f, 0); + } + } + return ret; +} + +guint16 * +fu_uft8_to_ucs2 (const gchar *str, gssize max) +{ + gssize i, j; + guint16 *ret = g_new0 (guint16, g_utf8_strlen (str, max) + 1); + for (i = 0, j = 0; i < (max >= 0 ? max : i + 1) && str[i] != '\0'; j++) { + guint32 val = 0; + if ((str[i] & 0xe0) == 0xe0 && !(str[i] & 0x10)) { + val = ((str[i+0] & 0x0f) << 10) + |((str[i+1] & 0x3f) << 6) + |((str[i+2] & 0x3f) << 0); + i += 3; + } else if ((str[i] & 0xc0) == 0xc0 && !(str[i] & 0x20)) { + val = ((str[i+0] & 0x1f) << 6) + |((str[i+1] & 0x3f) << 0); + i += 2; + } else { + val = str[i] & 0x7f; + i += 1; + } + ret[j] = val; + } + ret[j] = L'\0'; + return ret; +} + +gsize +fu_ucs2_strlen (const guint16 *str, gssize limit) +{ + gssize i; + for (i = 0; i < (limit >= 0 ? limit : i + 1) && str[i] != L'\0'; i++); + return i; +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-ucs2.h fwupd-1.2.10/plugins/uefi/fu-ucs2.h --- fwupd-1.0.9/plugins/uefi/fu-ucs2.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-ucs2.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gsize fu_ucs2_strlen (const guint16 *str, + gssize limit); +guint16 *fu_uft8_to_ucs2 (const gchar *str, + gssize max); +gchar *fu_ucs2_to_uft8 (const guint16 *str, + gssize max); diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-bgrt.c fwupd-1.2.10/plugins/uefi/fu-uefi-bgrt.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-bgrt.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-bgrt.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" +#include "fu-uefi-bgrt.h" +#include "fu-uefi-common.h" + +struct _FuUefiBgrt { + GObject parent_instance; + guint32 xoffset; + guint32 yoffset; + guint32 width; + guint32 height; +}; + +G_DEFINE_TYPE (FuUefiBgrt, fu_uefi_bgrt, G_TYPE_OBJECT) + +gboolean +fu_uefi_bgrt_setup (FuUefiBgrt *self, GError **error) +{ + gsize sz = 0; + guint64 type; + guint64 version; + g_autofree gchar *bgrtdir = NULL; + g_autofree gchar *data = NULL; + g_autofree gchar *imagefn = NULL; + g_autofree gchar *sysfsfwdir = NULL; + + g_return_val_if_fail (FU_IS_UEFI_BGRT (self), FALSE); + + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + bgrtdir = g_build_filename (sysfsfwdir, "acpi", "bgrt", NULL); + if (!g_file_test (bgrtdir, G_FILE_TEST_EXISTS)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "BGRT is not supported"); + return FALSE; + } + type = fu_uefi_read_file_as_uint64 (bgrtdir, "type"); + if (type != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "BGRT type was %" G_GUINT64_FORMAT, type); + return FALSE; + } + version = fu_uefi_read_file_as_uint64 (bgrtdir, "version"); + if (version != 1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "BGRT version was %" G_GUINT64_FORMAT, version); + return FALSE; + } + + /* load image */ + self->xoffset = fu_uefi_read_file_as_uint64 (bgrtdir, "xoffset"); + self->yoffset = fu_uefi_read_file_as_uint64 (bgrtdir, "yoffset"); + imagefn = g_build_filename (bgrtdir, "image", NULL); + if (!g_file_get_contents (imagefn, &data, &sz, error)) { + g_prefix_error (error, "failed to load BGRT image: "); + return FALSE; + } + if (!fu_uefi_get_bitmap_size ((guint8 *) data, sz, + &self->width, &self->height, error)) { + g_prefix_error (error, "BGRT image invalid: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_uefi_bgrt_get_supported (FuUefiBgrt *self) +{ + g_return_val_if_fail (FU_IS_UEFI_BGRT (self), FALSE); + if (self->width == 0 || self->height == 0) + return FALSE; + return TRUE; +} + +guint32 +fu_uefi_bgrt_get_xoffset (FuUefiBgrt *self) +{ + g_return_val_if_fail (FU_IS_UEFI_BGRT (self), 0); + return self->xoffset; +} + +guint32 +fu_uefi_bgrt_get_yoffset (FuUefiBgrt *self) +{ + g_return_val_if_fail (FU_IS_UEFI_BGRT (self), 0); + return self->yoffset; +} + +guint32 +fu_uefi_bgrt_get_width (FuUefiBgrt *self) +{ + g_return_val_if_fail (FU_IS_UEFI_BGRT (self), 0); + return self->width; +} + +guint32 +fu_uefi_bgrt_get_height (FuUefiBgrt *self) +{ + g_return_val_if_fail (FU_IS_UEFI_BGRT (self), 0); + return self->height; +} + +static void +fu_uefi_bgrt_class_init (FuUefiBgrtClass *klass) +{ +} + +static void +fu_uefi_bgrt_init (FuUefiBgrt *self) +{ +} + +FuUefiBgrt * +fu_uefi_bgrt_new (void) +{ + FuUefiBgrt *self; + self = g_object_new (FU_TYPE_UEFI_BGRT, NULL); + return FU_UEFI_BGRT (self); +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-bgrt.h fwupd-1.2.10/plugins/uefi/fu-uefi-bgrt.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-bgrt.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-bgrt.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +G_BEGIN_DECLS + +#define FU_TYPE_UEFI_BGRT (fu_uefi_bgrt_get_type ()) +G_DECLARE_FINAL_TYPE (FuUefiBgrt, fu_uefi_bgrt, FU, UEFI_BGRT, GObject) + +FuUefiBgrt *fu_uefi_bgrt_new (void); +gboolean fu_uefi_bgrt_setup (FuUefiBgrt *self, + GError **error); +gboolean fu_uefi_bgrt_get_supported (FuUefiBgrt *self); +guint32 fu_uefi_bgrt_get_xoffset (FuUefiBgrt *self); +guint32 fu_uefi_bgrt_get_yoffset (FuUefiBgrt *self); +guint32 fu_uefi_bgrt_get_width (FuUefiBgrt *self); +guint32 fu_uefi_bgrt_get_height (FuUefiBgrt *self); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-bootmgr.c fwupd-1.2.10/plugins/uefi/fu-uefi-bootmgr.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-bootmgr.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-bootmgr.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "fwupd-error.h" + +#include "fu-ucs2.h" +#include "fu-uefi-bootmgr.h" +#include "fu-uefi-common.h" + +/* XXX PJFIX: this should be in efiboot-loadopt.h in efivar */ +#define LOAD_OPTION_ACTIVE 0x00000001 + +static gboolean +fu_uefi_bootmgr_add_to_boot_order (guint16 boot_entry, GError **error) +{ + gsize boot_order_size = 0; + gint rc; + guint i = 0; + guint32 attr = EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + g_autofree guint16 *boot_order = NULL; + g_autofree guint16 *new_boot_order = NULL; + + /* get size of the BootOrder */ + rc = efi_get_variable_size (efi_guid_global, "BootOrder", &boot_order_size); + if (rc == ENOENT) { + boot_order_size = 0; + efi_error_clear (); + } else if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "efi_get_variable_size() failed"); + return rc; + } + + /* get the current boot order */ + if (boot_order_size != 0) { + rc = efi_get_variable (efi_guid_global, "BootOrder", + (guint8 **)&boot_order, &boot_order_size, + &attr); + if (rc < 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "efi_get_variable(BootOrder) failed"); + return FALSE; + } + + /* already set next */ + for (i = 0; i < boot_order_size / sizeof (guint16); i++) { + guint16 val = boot_order[i]; + if (val == boot_entry) + return TRUE; + } + } + + /* add the new boot index to the end of the list */ + new_boot_order = g_malloc0 (boot_order_size + sizeof (guint16)); + if (boot_order_size != 0) + memcpy (new_boot_order, boot_order, boot_order_size); + + i = boot_order_size / sizeof (guint16); + new_boot_order[i] = boot_entry; + boot_order_size += sizeof (guint16); + rc = efi_set_variable(efi_guid_global, "BootOrder", + (guint8 *)new_boot_order, boot_order_size, + attr, 0644); + if (rc < 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "efi_set_variable(BootOrder) failed"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_size, GError **error) +{ + efi_guid_t *guid = NULL; + efi_load_option *loadopt = NULL; + gchar *name = NULL; + gint rc; + gint set_entries[0x10000 / sizeof(gint)] = {0,}; + gsize var_data_size = 0; + guint16 real_boot16; + guint32 attr; + guint32 boot_next = 0x10000; + g_autofree guint8 *var_data = NULL; + + while ((rc = efi_get_next_variable_name (&guid, &name)) > 0) { + const gchar *desc; + gint div, mod; + gint scanned = 0; + guint16 entry = 0; + g_autofree guint8 *var_data_tmp = NULL; + + if (efi_guid_cmp (guid, &efi_guid_global) != 0) + continue; + rc = sscanf (name, "Boot%hX%n", &entry, &scanned); + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to parse Boot entry %s", name); + return FALSE; + } + if (rc != 1) + continue; + if (scanned != 8) + continue; + + div = entry / (sizeof(set_entries[0]) * 8); + mod = entry % (sizeof(set_entries[0]) * 8); + + set_entries[div] |= 1 << mod; + + rc = efi_get_variable (*guid, name, &var_data_tmp, &var_data_size, &attr); + if (rc < 0) { + g_debug ("efi_get_variable(%s) failed", name); + continue; + } + + loadopt = (efi_load_option *)var_data_tmp; + if (!efi_loadopt_is_valid(loadopt, var_data_size)) { + g_debug ("load option was invalid"); + continue; + } + + desc = (const gchar *) efi_loadopt_desc (loadopt, var_data_size); + if (g_strcmp0 (desc, "Linux Firmware Updater") != 0 && + g_strcmp0 (desc, "Linux-Firmware-Updater") != 0) { + g_debug ("description does not match"); + continue; + } + + var_data = g_steal_pointer (&var_data_tmp); + boot_next = entry; + efi_error_clear (); + break; + } + if (rc < 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to find boot variable"); + return FALSE; + } + + /* already exists */ + if (var_data != NULL) { + efi_loadopt_attr_set (loadopt, LOAD_OPTION_ACTIVE); + rc = efi_set_variable (*guid, name, var_data, + var_data_size, attr, 0644); + if (rc < 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not set boot variable active"); + return FALSE; + } + + /* create a new one */ + } else { + g_autofree gchar *boot_next_name = NULL; + for (guint32 value = 0; value < 0x10000; value++) { + gint div = value / (sizeof(set_entries[0]) * 8); + gint mod = value % (sizeof(set_entries[0]) * 8); + if (set_entries[div] & (1 << mod)) + continue; + boot_next = value; + break; + } + if (boot_next >= 0x10000) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "no free boot variables (tried %x)", + boot_next); + return FALSE; + } + boot_next_name = g_strdup_printf ("Boot%04X", + (guint) (boot_next & 0xffff)); + rc = efi_set_variable (efi_guid_global, boot_next_name, opt, opt_size, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 0644); + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not set boot variable %s: %d", + boot_next_name, rc); + return FALSE; + } + } + + /* TODO: conditionalize this on the UEFI version? */ + if(!fu_uefi_bootmgr_add_to_boot_order (boot_next, error)) + return FALSE; + + /* set the boot next */ + real_boot16 = boot_next; + rc = efi_set_variable (efi_guid_global, "BootNext", (guint8 *)&real_boot16, 2, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 0644); + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not set BootNext(%" G_GUINT16_FORMAT ")", + real_boot16); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_uefi_cmp_asset (const gchar *source, const gchar *target) +{ + gsize len = 0; + g_autofree gchar *source_checksum = NULL; + g_autofree gchar *source_data = NULL; + g_autofree gchar *target_checksum = NULL; + g_autofree gchar *target_data = NULL; + + /* nothing in target yet */ + if (!g_file_test (target, G_FILE_TEST_EXISTS)) + return FALSE; + + /* test if the file needs to be updated */ + if (!g_file_get_contents (source, &source_data, &len, NULL)) + return FALSE; + source_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256, + (guchar *) source_data, len); + if (!g_file_get_contents (target, &target_data, &len, NULL)) + return FALSE; + target_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256, + (guchar *) target_data, len); + return g_strcmp0 (target_checksum, source_checksum) == 0; +} + +static gboolean +fu_uefi_copy_asset (const gchar *source, const gchar *target, GError **error) +{ + g_autoptr(GFile) source_file = g_file_new_for_path (source); + g_autoptr(GFile) target_file = g_file_new_for_path (target); + + if (!g_file_copy (source_file, + target_file, + G_FILE_COPY_OVERWRITE, + NULL, + NULL, + NULL, + error)) { + g_prefix_error (error, "Failed to copy %s to %s: ", + source, target); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_uefi_bootmgr_bootnext (const gchar *esp_path, + const gchar *description, + FuUefiBootmgrFlags flags, + GError **error) +{ + const gchar *filepath; + gboolean use_fwup_path = FALSE; + gsize loader_sz = 0; + gssize opt_size = 0; + gssize sz, dp_size = 0; + guint32 attributes = LOAD_OPTION_ACTIVE; + g_autofree guint16 *loader_str = NULL; + g_autofree gchar *label = NULL; + g_autofree gchar *shim_app = NULL; + g_autofree gchar *shim_cpy = NULL; + g_autofree guint8 *dp_buf = NULL; + g_autofree guint8 *opt = NULL; + g_autofree gchar *source_app = NULL; + g_autofree gchar *target_app = NULL; + + /* skip for self tests */ + if (g_getenv ("FWUPD_UEFI_ESP_PATH") != NULL) + return TRUE; + + /* if secure boot was turned on this might need to be installed separately */ + source_app = fu_uefi_get_built_app_path (error); + if (source_app == NULL) + return FALSE; + + /* test to make sure shim is there if we need it */ + shim_app = fu_uefi_get_esp_app_path (esp_path, "shim", error); + if (shim_app == NULL) + return FALSE; + if (g_file_test (shim_app, G_FILE_TEST_EXISTS)) { + /* use a custom copy of shim for firmware updates */ + if (flags & FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE) { + shim_cpy = fu_uefi_get_esp_app_path (esp_path, "shimfwupd", error); + if (shim_cpy == NULL) + return FALSE; + if (!fu_uefi_cmp_asset (shim_app, shim_cpy)) { + if (!fu_uefi_copy_asset (shim_app, shim_cpy, error)) + return FALSE; + } + filepath = shim_cpy; + } else { + filepath = shim_app; + } + } else { + if (fu_uefi_secure_boot_enabled () && + (flags & FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB) > 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_BROKEN_SYSTEM, + "Secure boot is enabled, but shim isn't installed to the EFI system partition"); + return FALSE; + } + use_fwup_path = TRUE; + } + + /* test if correct asset in place */ + target_app = fu_uefi_get_esp_app_path (esp_path, "fwupd", error); + if (target_app == NULL) + return FALSE; + if (!fu_uefi_cmp_asset (source_app, target_app)) { + if (!fu_uefi_copy_asset (source_app, target_app, error)) + return FALSE; + } + + /* no shim, so use this directly */ + if (use_fwup_path) + filepath = target_app; + + /* generate device path for target */ + sz = efi_generate_file_device_path (dp_buf, dp_size, filepath, + EFIBOOT_OPTIONS_IGNORE_FS_ERROR| + EFIBOOT_ABBREV_HD); + if (sz < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "efi_generate_file_device_path(%s) failed", + filepath); + return FALSE; + } + + /* add the fwupdx64.efi ESP path as the shim loadopt data */ + dp_size = sz; + dp_buf = g_malloc0 (dp_size); + if (!use_fwup_path) { + g_autofree gchar *fwup_fs_basename = g_path_get_basename (target_app); + g_autofree gchar *fwup_esp_path = g_strdup_printf ("\\%s", fwup_fs_basename); + loader_str = fu_uft8_to_ucs2 (fwup_esp_path, -1); + loader_sz = fu_ucs2_strlen (loader_str, -1) * 2; + if (loader_sz) + loader_sz += 2; + } + + sz = efi_generate_file_device_path (dp_buf, dp_size, filepath, + EFIBOOT_OPTIONS_IGNORE_FS_ERROR| + EFIBOOT_ABBREV_HD); + if (sz != dp_size) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "efi_generate_file_device_path(%s) failed", + filepath); + return FALSE; + } + + label = g_strdup (description); + sz = efi_loadopt_create (opt, opt_size, attributes, + (efidp)dp_buf, dp_size, + (guint8 *)label, + (guint8 *)loader_str, loader_sz); + if (sz < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "efi_loadopt_create(%s) failed", + label); + return FALSE; + } + opt = g_malloc0 (sz); + opt_size = sz; + sz = efi_loadopt_create (opt, opt_size, attributes, + (efidp)dp_buf, dp_size, + (guint8 *)label, + (guint8 *)loader_str, loader_sz); + if (sz != opt_size) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "loadopt size was unreasonable."); + return FALSE; + } + if (!fu_uefi_setup_bootnext_with_dp (dp_buf, opt, opt_size, error)) + return FALSE; + efi_error_clear(); + + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-bootmgr.h fwupd-1.2.10/plugins/uefi/fu-uefi-bootmgr.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-bootmgr.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-bootmgr.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +typedef enum { + FU_UEFI_BOOTMGR_FLAG_NONE = 0, + FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB = 1 << 0, + FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE = 1 << 1, + FU_UEFI_BOOTMGR_FLAG_LAST +} FuUefiBootmgrFlags; + +gboolean fu_uefi_bootmgr_bootnext (const gchar *esp_path, + const gchar *description, + FuUefiBootmgrFlags flags, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-common.c fwupd-1.2.10/plugins/uefi/fu-uefi-common.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-common.h" +#include "fu-uefi-common.h" +#include "fu-uefi-vars.h" + +#include "fwupd-common.h" +#include "fwupd-error.h" + +#ifndef HAVE_GIO_2_55_0 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUnixMountEntry, g_unix_mount_free) +#pragma clang diagnostic pop +#endif + +static const gchar * +fu_uefi_bootmgr_get_suffix (GError **error) +{ + guint64 firmware_bits; + struct { + guint64 bits; + const gchar *arch; + } suffixes[] = { +#if defined(__x86_64__) + { 64, "x64" }, +#elif defined(__aarch64__) + { 64, "aa64" }, +#endif +#if defined(__x86_64__) || defined(__i386__) || defined(__i686__) + { 32, "ia32" }, +#endif + { 0, NULL } + }; + g_autofree gchar *sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + g_autofree gchar *sysfsefidir = g_build_filename (sysfsfwdir, "efi", NULL); + firmware_bits = fu_uefi_read_file_as_uint64 (sysfsefidir, "fw_platform_size"); + if (firmware_bits == 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "%s/fw_platform_size cannot be found", + sysfsefidir); + return NULL; + } + for (guint i = 0; suffixes[i].arch != NULL; i++) { + if (firmware_bits != suffixes[i].bits) + continue; + return suffixes[i].arch; + } + + /* this should exist */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "%s/fw_platform_size has unknown value %" G_GUINT64_FORMAT, + sysfsefidir, firmware_bits); + return NULL; +} + +gchar * +fu_uefi_get_esp_app_path (const gchar *esp_path, const gchar *cmd, GError **error) +{ + const gchar *suffix = fu_uefi_bootmgr_get_suffix (error); + g_autofree gchar *base = NULL; + if (suffix == NULL) + return NULL; + base = fu_uefi_get_esp_path_for_os (esp_path); + return g_strdup_printf ("%s/%s%s.efi", base, cmd, suffix); +} + +gchar * +fu_uefi_get_built_app_path (GError **error) +{ + const gchar *extension = ""; + const gchar *suffix; + g_autofree gchar *source_path = NULL; + g_autofree gchar *prefix = NULL; + if (fu_uefi_secure_boot_enabled ()) + extension = ".signed"; + suffix = fu_uefi_bootmgr_get_suffix (error); + if (suffix == NULL) + return NULL; + prefix = fu_common_get_path (FU_PATH_KIND_EFIAPPDIR); + source_path = g_strdup_printf ("%s/fwupd%s.efi%s", + prefix, + suffix, + extension); + if (!g_file_test (source_path, G_FILE_TEST_EXISTS)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "%s cannot be found", + source_path); + return NULL; + } + return g_steal_pointer (&source_path); +} + +gboolean +fu_uefi_get_framebuffer_size (guint32 *width, guint32 *height, GError **error) +{ + guint32 height_tmp; + guint32 width_tmp; + g_autofree gchar *sysfsdriverdir = NULL; + g_autofree gchar *fbdir = NULL; + + sysfsdriverdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_DRIVERS); + fbdir = g_build_filename (sysfsdriverdir, "efi-framebuffer", "efi-framebuffer.0", NULL); + if (!g_file_test (fbdir, G_FILE_TEST_EXISTS)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "EFI framebuffer not found"); + return FALSE; + } + height_tmp = fu_uefi_read_file_as_uint64 (fbdir, "height"); + width_tmp = fu_uefi_read_file_as_uint64 (fbdir, "width"); + if (width_tmp == 0 || height_tmp == 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "EFI framebuffer has invalid size " + "%"G_GUINT32_FORMAT"x%"G_GUINT32_FORMAT, + width_tmp, height_tmp); + return FALSE; + } + if (width != NULL) + *width = width_tmp; + if (height != NULL) + *height = height_tmp; + return TRUE; +} + +gboolean +fu_uefi_get_bitmap_size (const guint8 *buf, + gsize bufsz, + guint32 *width, + guint32 *height, + GError **error) +{ + guint32 ui32; + + g_return_val_if_fail (buf != NULL, FALSE); + + /* check header */ + if (bufsz < 26) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "blob was too small %" G_GSIZE_FORMAT, bufsz); + return FALSE; + } + if (memcmp (buf, "BM", 2) != 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid BMP header signature"); + return FALSE; + } + + /* starting address */ + ui32 = fu_common_read_uint32 (buf + 10, G_LITTLE_ENDIAN); + if (ui32 < 26) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "BMP header invalid @ %"G_GUINT32_FORMAT"x", ui32); + return FALSE; + } + + /* BITMAPINFOHEADER header */ + ui32 = fu_common_read_uint32 (buf + 14, G_LITTLE_ENDIAN); + if (ui32 < 26 - 14) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "BITMAPINFOHEADER invalid @ %"G_GUINT32_FORMAT"x", ui32); + return FALSE; + } + + /* dimensions */ + if (width != NULL) + *width = fu_common_read_uint32 (buf + 18, G_LITTLE_ENDIAN); + if (height != NULL) + *height = fu_common_read_uint32 (buf + 22, G_LITTLE_ENDIAN); + return TRUE; +} + +gboolean +fu_uefi_secure_boot_enabled (void) +{ + gsize data_size = 0; + g_autofree guint8 *data = NULL; + + if (!fu_uefi_vars_get_data (FU_UEFI_VARS_GUID_EFI_GLOBAL, "SecureBoot", + &data, &data_size, NULL, NULL)) + return FALSE; + if (data_size >= 1 && data[0] & 1) + return TRUE; + return FALSE; +} + +static gint +fu_uefi_strcmp_sort_cb (gconstpointer a, gconstpointer b) +{ + const gchar *stra = *((const gchar **) a); + const gchar *strb = *((const gchar **) b); + return g_strcmp0 (stra, strb); +} + +GPtrArray * +fu_uefi_get_esrt_entry_paths (const gchar *esrt_path, GError **error) +{ + GPtrArray *entries = g_ptr_array_new_with_free_func (g_free); + const gchar *fn; + g_autofree gchar *esrt_entries = NULL; + g_autoptr(GDir) dir = NULL; + + /* search ESRT */ + esrt_entries = g_build_filename (esrt_path, "entries", NULL); + dir = g_dir_open (esrt_entries, 0, error); + if (dir == NULL) + return NULL; + while ((fn = g_dir_read_name (dir)) != NULL) + g_ptr_array_add (entries, g_build_filename (esrt_entries, fn, NULL)); + + /* sort by name */ + g_ptr_array_sort (entries, fu_uefi_strcmp_sort_cb); + return entries; +} + +gchar * +fu_uefi_get_esp_path_for_os (const gchar *esp_path) +{ + const gchar *os_release_id = NULL; +#ifndef EFI_OS_DIR + g_autoptr(GError) error_local = NULL; + g_autoptr(GHashTable) os_release = fwupd_get_os_release (&error_local); + if (os_release != NULL) { + os_release_id = g_hash_table_lookup (os_release, "ID"); + } else { + g_debug ("failed to get ID: %s", error_local->message); + } + if (os_release_id == NULL) + os_release_id = "unknown"; +#else + os_release_id = EFI_OS_DIR; +#endif + return g_build_filename (esp_path, "EFI", os_release_id, NULL); +} + +guint64 +fu_uefi_read_file_as_uint64 (const gchar *path, const gchar *attr_name) +{ + g_autofree gchar *data = NULL; + g_autofree gchar *fn = g_build_filename (path, attr_name, NULL); + if (!g_file_get_contents (fn, &data, NULL, NULL)) + return 0x0; + return fu_common_strtoull (data); +} + +gboolean +fu_uefi_check_esp_free_space (const gchar *path, guint64 required, GError **error) +{ + guint64 fs_free; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + + file = g_file_new_for_path (path); + info = g_file_query_filesystem_info (file, + G_FILE_ATTRIBUTE_FILESYSTEM_FREE, + NULL, error); + if (info == NULL) + return FALSE; + fs_free = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); + if (fs_free < required) { + g_autofree gchar *str_free = g_format_size (fs_free); + g_autofree gchar *str_reqd = g_format_size (required); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s does not have sufficient space, required %s, got %s", + path, str_reqd, str_free); + return FALSE; + } + return TRUE; +} + +gboolean +fu_uefi_check_esp_path (const gchar *path, GError **error) +{ + const gchar *fs_types[] = { "vfat", "ntfs", "exfat", "autofs", NULL }; + g_autoptr(GUnixMountEntry) mount = g_unix_mount_at (path, NULL); + if (mount == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "%s was not mounted", path); + return FALSE; + } + + /* /boot is a special case because systemd sandboxing marks + * it read-only, but we need to write to /boot/EFI + */ + if (g_strcmp0 (path, "/boot") == 0) { + if (!g_file_test ("/boot/EFI", G_FILE_TEST_IS_DIR)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s/EFI does not exist", path); + return FALSE; + } + /* /efi is a special case because systemd sandboxing marks + * it read-only, but we need to write to /efi/EFI + */ + } else if (g_strcmp0 (path, "/efi") == 0) { + if (!g_file_test ("/efi/EFI", G_FILE_TEST_IS_DIR)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s/EFI does not exist", path); + return FALSE; + } + } else if (g_unix_mount_is_readonly (mount)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is read only", path); + return FALSE; + } + if (!g_strv_contains (fs_types, g_unix_mount_get_fs_type (mount))) { + g_autofree gchar *supported = g_strjoinv ("|", (gchar **) fs_types); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s has an invalid type, expected %s", + path, supported); + return FALSE; + } + return TRUE; +} + +gchar * +fu_uefi_guess_esp_path (void) +{ + const gchar *paths[] = {"/boot/efi", "/boot", "/efi", NULL}; + const gchar *path_tmp; + + /* for the test suite use local directory for ESP */ + path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH"); + if (path_tmp != NULL) + return g_strdup (path_tmp); + + for (guint i = 0; paths[i] != NULL; i++) { + g_autoptr(GError) error = NULL; + if (!fu_uefi_check_esp_path (paths[i], &error)) { + g_debug ("ignoring ESP path: %s", error->message); + continue; + } + return g_strdup (paths[i]); + } + + return NULL; +} + +gboolean +fu_uefi_prefix_efi_errors (GError **error) +{ + g_autoptr(GString) str = g_string_new (NULL); + for (gint i = 0; ; i++) { + gchar *filename = NULL; + gchar *function = NULL; + gchar *message = NULL; + gint line = 0; + gint err = 0; + if (efi_error_get (i, &filename, &function, &line, + &message, &err) <= 0) + break; + g_string_append_printf (str, "{error #%d} %s:%d %s(): %s: %s\t", + i, filename, line, function, + message, strerror (err)); + } + if (str->len > 1) + g_string_truncate (str, str->len - 1); + g_prefix_error (error, "%s: ", str->str); + return FALSE; +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-common.h fwupd-1.2.10/plugins/uefi/fu-uefi-common.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET 0x00010000 +#define EFI_CAPSULE_HEADER_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 +#define EFI_CAPSULE_HEADER_FLAGS_INITIATE_RESET 0x00040000 + +typedef struct __attribute__((__packed__)) { + guint16 year; + guint8 month; + guint8 day; + guint8 hour; + guint8 minute; + guint8 second; + guint8 pad1; + guint32 nanosecond; + guint16 timezone; + guint8 daylight; + guint8 pad2; +} efi_time_t; + +typedef struct __attribute__((__packed__)) { + efi_guid_t guid; + guint32 header_size; + guint32 flags; + guint32 capsule_image_size; +} efi_capsule_header_t; + +typedef struct __attribute__((__packed__)) { + guint8 version; + guint8 checksum; + guint8 image_type; + guint8 reserved; + guint32 mode; + guint32 x_offset; + guint32 y_offset; +} efi_ux_capsule_header_t; + +typedef struct __attribute__((__packed__)) { + guint32 update_info_version; + efi_guid_t guid; + guint32 capsule_flags; + guint64 hw_inst; + efi_time_t time_attempted; + guint32 status; +} efi_update_info_t; + +/* the biggest size SPI part currently seen */ +#define FU_UEFI_COMMON_REQUIRED_ESP_FREE_SPACE (32 * 1024 * 1024) + +gchar *fu_uefi_get_esp_app_path (const gchar *esp_path, + const gchar *cmd, + GError **error); +gchar *fu_uefi_get_built_app_path (GError **error); +gboolean fu_uefi_get_bitmap_size (const guint8 *buf, + gsize bufsz, + guint32 *width, + guint32 *height, + GError **error); +gboolean fu_uefi_get_framebuffer_size (guint32 *width, + guint32 *height, + GError **error); +gboolean fu_uefi_secure_boot_enabled (void); +gchar *fu_uefi_guess_esp_path (void); +gboolean fu_uefi_check_esp_path (const gchar *path, + GError **error); +gboolean fu_uefi_check_esp_free_space (const gchar *path, + guint64 required, + GError **error); +gchar *fu_uefi_get_esp_path_for_os (const gchar *esp_path); +GPtrArray *fu_uefi_get_esrt_entry_paths (const gchar *esrt_path, + GError **error); +guint64 fu_uefi_read_file_as_uint64 (const gchar *path, + const gchar *attr_name); +gboolean fu_uefi_prefix_efi_errors (GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-device.c fwupd-1.2.10/plugins/uefi/fu-uefi-device.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include + +#include "fu-device-metadata.h" + +#include "fu-uefi-common.h" +#include "fu-uefi-device.h" +#include "fu-uefi-devpath.h" +#include "fu-uefi-bootmgr.h" +#include "fu-uefi-pcrs.h" +#include "fu-uefi-vars.h" + +struct _FuUefiDevice { + FuDevice parent_instance; + gchar *fw_class; + FuUefiDeviceKind kind; + guint32 capsule_flags; + guint32 fw_version; + guint32 fw_version_lowest; + 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) + +const gchar * +fu_uefi_device_kind_to_string (FuUefiDeviceKind kind) +{ + if (kind == FU_UEFI_DEVICE_KIND_UNKNOWN) + return "unknown"; + if (kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) + return "system-firmware"; + if (kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) + return "device-firmware"; + if (kind == FU_UEFI_DEVICE_KIND_UEFI_DRIVER) + return "uefi-driver"; + if (kind == FU_UEFI_DEVICE_KIND_FMP) + return "fmp"; + if (kind == FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE) + return "dell-tpm-firmware"; + return NULL; +} + +static FuUefiDeviceKind +fu_uefi_device_kind_from_string (const gchar *kind) +{ + if (g_strcmp0 (kind, "system-firmware") == 0) + return FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE; + if (g_strcmp0 (kind, "device-firmware") == 0) + return FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE; + if (g_strcmp0 (kind, "uefi-driver") == 0) + return FU_UEFI_DEVICE_KIND_UEFI_DRIVER; + if (g_strcmp0 (kind, "fmp") == 0) + return FU_UEFI_DEVICE_KIND_FMP; + if (g_strcmp0 (kind, "dell-tpm-firmware") == 0) + return FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE; + return FU_UEFI_DEVICE_KIND_UNKNOWN; +} + +const gchar * +fu_uefi_device_status_to_string (FuUefiDeviceStatus status) +{ + if (status == FU_UEFI_DEVICE_STATUS_SUCCESS) + return "success"; + if (status == FU_UEFI_DEVICE_STATUS_ERROR_UNSUCCESSFUL) + return "unsuccessful"; + if (status == FU_UEFI_DEVICE_STATUS_ERROR_INSUFFICIENT_RESOURCES) + return "insufficient resources"; + if (status == FU_UEFI_DEVICE_STATUS_ERROR_INCORRECT_VERSION) + return "incorrect version"; + if (status == FU_UEFI_DEVICE_STATUS_ERROR_INVALID_FORMAT) + return "invalid firmware format"; + if (status == FU_UEFI_DEVICE_STATUS_ERROR_AUTH_ERROR) + return "authentication signing error"; + if (status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_AC) + return "AC power required"; + if (status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT) + return "battery level is too low"; + return NULL; +} + +static void +fu_uefi_device_to_string (FuDevice *device, GString *str) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + g_string_append (str, " FuUefiDevice:\n"); + g_string_append_printf (str, " kind:\t\t\t%s\n", + fu_uefi_device_kind_to_string (self->kind)); + g_string_append_printf (str, " fw_class:\t\t\t%s\n", self->fw_class); + g_string_append_printf (str, " capsule_flags:\t\t%" G_GUINT32_FORMAT "\n", + self->capsule_flags); + g_string_append_printf (str, " fw_version:\t\t\t%" G_GUINT32_FORMAT "\n", + self->fw_version); + g_string_append_printf (str, " fw_version_lowest:\t\t%" G_GUINT32_FORMAT "\n", + self->fw_version_lowest); + g_string_append_printf (str, " last_attempt_status:\t%s\n", + fu_uefi_device_status_to_string (self->last_attempt_status)); + g_string_append_printf (str, " last_attempt_version:\t%" G_GUINT32_FORMAT "\n", + self->last_attempt_version); + g_string_append_printf (str, " esp path:\t%s\n", + fu_device_get_metadata (device, "EspPath")); +} + +FuUefiDeviceKind +fu_uefi_device_get_kind (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), 0); + return self->kind; +} + +guint32 +fu_uefi_device_get_version (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), 0x0); + return self->fw_version; +} + +guint32 +fu_uefi_device_get_version_lowest (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), 0x0); + return self->fw_version_lowest; +} + +guint32 +fu_uefi_device_get_version_error (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), 0x0); + return self->last_attempt_version; +} + +guint64 +fu_uefi_device_get_hardware_instance (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), 0x0); + return self->fmp_hardware_instance; +} + +FuUefiDeviceStatus +fu_uefi_device_get_status (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), 0); + return self->last_attempt_status; +} + +guint32 +fu_uefi_device_get_capsule_flags (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), 0x0); + return self->capsule_flags; +} + +const gchar * +fu_uefi_device_get_guid (FuUefiDevice *self) +{ + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), NULL); + return self->fw_class; +} + +static gchar * +fu_uefi_device_build_varname (FuUefiDevice *self) +{ + return g_strdup_printf ("fwupd-%s-%"G_GUINT64_FORMAT, + self->fw_class, + self->fmp_hardware_instance); +} + +FuUefiUpdateInfo * +fu_uefi_device_load_update_info (FuUefiDevice *self, GError **error) +{ + gsize datasz = 0; + g_autofree gchar *varname = fu_uefi_device_build_varname (self); + g_autofree guint8 *data = NULL; + g_autoptr(FuUefiUpdateInfo) info = fu_uefi_update_info_new (); + + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* get the existing status */ + if (!fu_uefi_vars_get_data (FU_UEFI_VARS_GUID_FWUPDATE, varname, + &data, &datasz, NULL, error)) + return NULL; + if (!fu_uefi_update_info_parse (info, data, datasz, error)) + return NULL; + return g_steal_pointer (&info); +} + +gboolean +fu_uefi_device_clear_status (FuUefiDevice *self, GError **error) +{ + efi_update_info_t info; + gsize datasz = 0; + g_autofree gchar *varname = fu_uefi_device_build_varname (self); + g_autofree guint8 *data = NULL; + + g_return_val_if_fail (FU_IS_UEFI_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* get the existing status */ + if (!fu_uefi_vars_get_data (FU_UEFI_VARS_GUID_FWUPDATE, varname, + &data, &datasz, NULL, error)) + return FALSE; + if (datasz < sizeof(efi_update_info_t)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "EFI variable is corrupt"); + return FALSE; + } + + /* just copy the efi_update_info_t, ignore devpath then save it back */ + memcpy (&info, data, sizeof(info)); + info.status = FU_UEFI_DEVICE_STATUS_SUCCESS; + memcpy (data, &info, sizeof(info)); + return fu_uefi_vars_set_data (FU_UEFI_VARS_GUID_FWUPDATE, varname, + data, datasz, + FU_UEFI_VARS_ATTR_NON_VOLATILE | + FU_UEFI_VARS_ATTR_BOOTSERVICE_ACCESS | + FU_UEFI_VARS_ATTR_RUNTIME_ACCESS, + error); +} + +static guint8 * +fu_uefi_device_build_dp_buf (const gchar *path, gsize *bufsz, GError **error) +{ + gssize req; + gssize sz; + g_autofree guint8 *dp_buf = NULL; + g_autoptr(GPtrArray) dps = NULL; + + /* get the size of the path first */ + req = efi_generate_file_device_path (NULL, 0, path, + EFIBOOT_OPTIONS_IGNORE_FS_ERROR | + EFIBOOT_ABBREV_HD); + if (req < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to efi_generate_file_device_path(%s)", + path); + return NULL; + } + + /* if we just have an end device path, it's not going to work */ + if (req <= 4) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to get valid device_path for (%s)", + path); + return NULL; + } + + /* actually get the path this time */ + dp_buf = g_malloc0 (req); + sz = efi_generate_file_device_path (dp_buf, req, path, + EFIBOOT_OPTIONS_IGNORE_FS_ERROR | + EFIBOOT_ABBREV_HD); + if (sz < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to efi_generate_file_device_path(%s)", + path); + return NULL; + } + + /* parse what we got back from efivar */ + dps = fu_uefi_devpath_parse (dp_buf, (gsize) sz, + FU_UEFI_DEVPATH_PARSE_FLAG_NONE, error); + if (dps == NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "dp_buf", dp_buf, (gsize) sz); + return NULL; + } + + /* success */ + if (bufsz != NULL) + *bufsz = sz; + 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; +} + +gboolean +fu_uefi_device_write_update_info (FuUefiDevice *self, + const gchar *filename, + const gchar *varname, + const efi_guid_t *guid, + GError **error) +{ + gsize datasz = 0; + gsize dp_bufsz = 0; + g_autofree guint8 *data = NULL; + g_autofree guint8 *dp_buf = NULL; + efi_update_info_t info = { + .update_info_version = 0x7, + .guid = { 0x0 }, + .capsule_flags = self->capsule_flags, + .hw_inst = self->fmp_hardware_instance, + .time_attempted = { 0x0 }, + .status = FU_UEFI_UPDATE_INFO_STATUS_ATTEMPT_UPDATE, + }; + + /* set the body as the device path */ + if (g_getenv ("FWUPD_UEFI_ESP_PATH") != NULL) { + g_debug ("not building device path, in tests...."); + return TRUE; + } + + /* convert to EFI device path */ + dp_buf = fu_uefi_device_build_dp_buf (filename, &dp_bufsz, error); + if (dp_buf == NULL) { + fu_uefi_prefix_efi_errors (error); + return FALSE; + } + + /* save this header and body to the hardware */ + memcpy (&info.guid, guid, sizeof(efi_guid_t)); + datasz = sizeof(info) + dp_bufsz; + data = g_malloc0 (datasz); + memcpy (data, &info, sizeof(info)); + memcpy (data + sizeof(info), dp_buf, dp_bufsz); + if (!fu_uefi_vars_set_data (FU_UEFI_VARS_GUID_FWUPDATE, varname, + data, datasz, + FU_UEFI_VARS_ATTR_NON_VOLATILE | + FU_UEFI_VARS_ATTR_BOOTSERVICE_ACCESS | + FU_UEFI_VARS_ATTR_RUNTIME_ACCESS, + error)) { + fu_uefi_prefix_efi_errors (error); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_uefi_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags install_flags, + 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; + g_autoptr(GBytes) fixed_fw = NULL; + g_autofree gchar *basename = NULL; + g_autofree gchar *directory = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *varname = fu_uefi_device_build_varname (self); + + /* ensure we have the existing state */ + if (self->fw_class == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot update device info with no GUID"); + return FALSE; + } + + /* save the blob to the ESP */ + directory = fu_uefi_get_esp_path_for_os (esp_path); + basename = g_strdup_printf ("fwupd-%s.cap", self->fw_class); + fn = g_build_filename (directory, "fw", basename, NULL); + if (!fu_common_mkdir_parent (fn, error)) + return FALSE; + 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 */ + if (efi_str_to_guid (self->fw_class, &guid) < 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to get convert GUID"); + return FALSE; + } + if (!fu_uefi_device_write_update_info (self, fn, varname, &guid, error)) + return FALSE; + + /* 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_device_has_custom_flag (device, "use-shim-unique")) + flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE; + + /* 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); + FwupdVersionFormat version_format; + g_autofree gchar *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, version_format); + 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_instance_id (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); + devid = g_strdup_printf ("UEFI\\RES_{%s}", guid_strup); + fu_device_add_instance_id (device, devid); + return TRUE; +} + +static void +fu_uefi_device_init (FuUefiDevice *self) +{ +} + +static void +fu_uefi_device_finalize (GObject *object) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (object); + + g_free (self->fw_class); + + G_OBJECT_CLASS (fu_uefi_device_parent_class)->finalize (object); +} + +static void +fu_uefi_device_class_init (FuUefiDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (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; +} + +FuUefiDevice * +fu_uefi_device_new_from_entry (const gchar *entry_path, GError **error) +{ + g_autoptr(FuUefiDevice) self = NULL; + g_autofree gchar *fw_class_fn = NULL; + g_autofree gchar *id = NULL; + + g_return_val_if_fail (entry_path != NULL, NULL); + + /* create object */ + self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL); + + /* 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)) + g_strdelimit (self->fw_class, "\n", '\0'); + 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"); + self->last_attempt_status = fu_uefi_read_file_as_uint64 (entry_path, "last_attempt_status"); + self->last_attempt_version = fu_uefi_read_file_as_uint64 (entry_path, "last_attempt_version"); + self->fw_version_lowest = fu_uefi_read_file_as_uint64 (entry_path, "lowest_supported_fw_version"); + + /* the hardware instance is not in the ESRT table and we should really + * write the EFI stub to query with FMP -- but we still have not ever + * seen a PCIe device with FMP support... */ + self->fmp_hardware_instance = 0x0; + + /* set ID */ + id = g_strdup_printf ("UEFI-%s-dev%" G_GUINT64_FORMAT, + self->fw_class, self->fmp_hardware_instance); + fu_device_set_id (FU_DEVICE (self), id); + + /* this is invalid */ + if (!fwupd_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 g_steal_pointer (&self); +} + +FuUefiDevice * +fu_uefi_device_new_from_dev (FuDevice *dev) +{ + const gchar *tmp; + FuUefiDevice *self; + + g_return_val_if_fail (fu_device_get_guid_default (dev) != NULL, NULL); + + /* create virtual object not backed by an ESRT entry */ + self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), dev); + self->fw_class = g_strdup (fu_device_get_guid_default (dev)); + tmp = fu_device_get_metadata (dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND); + self->kind = fu_uefi_device_kind_from_string (tmp); + self->capsule_flags = fu_device_get_metadata_integer (dev, FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS); + self->fw_version = fu_device_get_metadata_integer (dev, FU_DEVICE_METADATA_UEFI_FW_VERSION); + g_assert (self->fw_class != NULL); + return self; +} + +FuUefiDevice * +fu_uefi_device_new_from_guid (const gchar *guid) +{ + FuUefiDevice *self; + self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL); + self->fw_class = g_strdup (guid); + return self; +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-device.h fwupd-1.2.10/plugins/uefi/fu-uefi-device.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" +#include "fu-uefi-device.h" +#include "fu-uefi-update-info.h" + +G_BEGIN_DECLS + +#define FU_TYPE_UEFI_DEVICE (fu_uefi_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuUefiDevice, fu_uefi_device, FU, UEFI_DEVICE, FuDevice) + +typedef enum { + FU_UEFI_DEVICE_KIND_UNKNOWN, + FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE, + FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE, + FU_UEFI_DEVICE_KIND_UEFI_DRIVER, + FU_UEFI_DEVICE_KIND_FMP, + FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE, + FU_UEFI_DEVICE_KIND_LAST +} FuUefiDeviceKind; + +typedef enum { + FU_UEFI_DEVICE_STATUS_SUCCESS = 0x00, + FU_UEFI_DEVICE_STATUS_ERROR_UNSUCCESSFUL = 0x01, + FU_UEFI_DEVICE_STATUS_ERROR_INSUFFICIENT_RESOURCES = 0x02, + FU_UEFI_DEVICE_STATUS_ERROR_INCORRECT_VERSION = 0x03, + FU_UEFI_DEVICE_STATUS_ERROR_INVALID_FORMAT = 0x04, + FU_UEFI_DEVICE_STATUS_ERROR_AUTH_ERROR = 0x05, + FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_AC = 0x06, + FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT = 0x07, + FU_UEFI_DEVICE_STATUS_LAST +} FuUefiDeviceStatus; + +FuUefiDevice *fu_uefi_device_new_from_guid (const gchar *guid); +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); +FuUefiDeviceKind fu_uefi_device_get_kind (FuUefiDevice *self); +const gchar *fu_uefi_device_get_guid (FuUefiDevice *self); +guint32 fu_uefi_device_get_version (FuUefiDevice *self); +guint32 fu_uefi_device_get_version_lowest (FuUefiDevice *self); +guint32 fu_uefi_device_get_version_error (FuUefiDevice *self); +guint32 fu_uefi_device_get_capsule_flags (FuUefiDevice *self); +guint64 fu_uefi_device_get_hardware_instance (FuUefiDevice *self); +FuUefiDeviceStatus fu_uefi_device_get_status (FuUefiDevice *self); +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); +gboolean fu_uefi_device_write_update_info (FuUefiDevice *self, + const gchar *filename, + const gchar *varname, + const efi_guid_t *guid, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-devpath.c fwupd-1.2.10/plugins/uefi/fu-uefi-devpath.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-devpath.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-devpath.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-uefi-devpath.h" + +#include "fwupd-error.h" + +typedef struct { + guint8 type; + guint8 subtype; + GBytes *data; +} FuUefiDevPath; + +static void +fu_uefi_efi_dp_free (FuUefiDevPath *dp) +{ + if (dp->data != NULL) + g_bytes_unref (dp->data); + g_free (dp); +} + +GBytes * +fu_uefi_devpath_find_data (GPtrArray *dps, guint8 type, guint8 subtype, GError **error) +{ + for (guint i = 0; i < dps->len; i++) { + FuUefiDevPath *dp = g_ptr_array_index (dps, i); + if (dp->type == type && dp->subtype == subtype) + return dp->data; + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no DP with type 0x%02x and subtype 0x%02x", + type, subtype); + return NULL; +} + +GPtrArray * +fu_uefi_devpath_parse (const guint8 *buf, gsize sz, + FuUefiDevpathParseFlags flags, GError **error) +{ + guint16 offset = 0; + g_autoptr(GPtrArray) dps = NULL; + + /* sanity check */ + if (sz < sizeof(efidp_header)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "const_efidp is corrupt"); + return NULL; + } + + dps = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_uefi_efi_dp_free); + while (1) { + FuUefiDevPath *dp; + const efidp_header *hdr = (efidp_header *) (buf + offset); + guint16 hdr_length = GUINT16_FROM_LE(hdr->length); + + /* check if last entry */ + g_debug ("DP type:0x%02x subtype:0x%02x size:0x%04x", + hdr->type, hdr->subtype, hdr->length); + if (hdr->type == EFIDP_END_TYPE && hdr->subtype == EFIDP_END_ENTIRE) + break; + + /* work around a bug in efi_va_generate_file_device_path_from_esp */ + if (offset + sizeof(efidp_header) + hdr->length > sz) { + hdr_length = 0; + fu_common_dump_full (G_LOG_DOMAIN, "efidp", + buf + offset, sz - offset, 32, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + for (guint16 i = offset + 4; i <= sz - 4; i++) { + if (memcmp (buf + i, "\x7f\xff\x04\x00", 4) == 0) { + hdr_length = i - offset; + g_debug ("found END_ENTIRE at 0x%04x", + (guint) (i - offset)); + break; + } + } + if (hdr_length == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "DP length invalid and no END_ENTIRE " + "found, possibly data truncation?"); + return NULL; + } + if ((flags & FU_UEFI_DEVPATH_PARSE_FLAG_REPAIR) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "DP length invalid, reported 0x%04x, maybe 0x%04x", + hdr->length, hdr_length); + return NULL; + } + g_debug ("DP length invalid! Truncating from 0x%04x to 0x%04x", + hdr->length, hdr_length); + } + + /* add new DP */ + dp = g_new0 (FuUefiDevPath, 1); + dp->type = hdr->type; + dp->subtype = hdr->subtype; + if (hdr_length > 0) + dp->data = g_bytes_new (buf + offset + 4, hdr_length); + g_ptr_array_add (dps, dp); + + /* advance to next DP */ + offset += hdr_length; + if (offset + sizeof(efidp_header) > sz) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "DP length invalid after fixing"); + return NULL; + } + + } + return g_steal_pointer (&dps); +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-devpath.h fwupd-1.2.10/plugins/uefi/fu-uefi-devpath.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-devpath.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-devpath.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +typedef enum { + FU_UEFI_DEVPATH_PARSE_FLAG_NONE = 0, + FU_UEFI_DEVPATH_PARSE_FLAG_REPAIR = 1 << 0, + FU_UEFI_DEVPATH_PARSE_FLAG_LAST +} FuUefiDevpathParseFlags; + +GPtrArray *fu_uefi_devpath_parse (const guint8 *buf, + gsize sz, + FuUefiDevpathParseFlags flags, + GError **error); +GBytes *fu_uefi_devpath_find_data (GPtrArray *dps, + guint8 type, + guint8 subtype, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-pcrs.c fwupd-1.2.10/plugins/uefi/fu-uefi-pcrs.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-pcrs.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-pcrs.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,217 @@ +/* + * 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 gboolean +_g_string_isxdigit (GString *str) +{ + for (gsize i = 0; i < str->len; i++) { + if (!g_ascii_isxdigit (str->str[i])) + return FALSE; + } + return TRUE; +} + +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, ":", -1); + 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]); + fu_common_string_replace (str, " ", ""); + if ((str->len != 40 && str->len != 64) || !_g_string_isxdigit (str)) { + g_debug ("not SHA-1 or SHA-256, skipping: %s", split[1]); + return; + } + 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, 1500, 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 -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-pcrs.h fwupd-1.2.10/plugins/uefi/fu-uefi-pcrs.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-pcrs.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-pcrs.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +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 diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-tool.c fwupd-1.2.10/plugins/uefi/fu-uefi-tool.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-tool.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-tool.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "fu-ucs2.h" +#include "fu-uefi-common.h" +#include "fu-uefi-device.h" +#include "fu-uefi-update-info.h" +#include "fu-uefi-vars.h" + +/* custom return code */ +#define EXIT_NOTHING_TO_DO 2 + +typedef struct { + GCancellable *cancellable; + GMainLoop *loop; + GOptionContext *context; +} FuUtilPrivate; + +static void +fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer user_data) +{ +} + +static void +fu_util_private_free (FuUtilPrivate *priv) +{ + if (priv->context != NULL) + g_option_context_free (priv->context); + g_free (priv); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) +#pragma clang diagnostic pop + +int +main (int argc, char *argv[]) +{ + gboolean action_enable = FALSE; + gboolean action_info = FALSE; + gboolean action_list = FALSE; + gboolean action_log = FALSE; + gboolean action_set_debug = FALSE; + gboolean action_supported = FALSE; + gboolean action_unset_debug = FALSE; + gboolean action_version = FALSE; + gboolean ret; + gboolean verbose = FALSE; + g_autofree gchar *apply = FALSE; + g_autofree gchar *esp_path = NULL; + g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + const GOptionEntry options[] = { + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + /* TRANSLATORS: command line option */ + _("Show extra debugging information"), NULL }, + { "version", '\0', 0, G_OPTION_ARG_NONE, &action_version, + /* TRANSLATORS: command line option */ + _("Display version"), NULL }, + { "log", 'L', 0, G_OPTION_ARG_NONE, &action_log, + /* TRANSLATORS: command line option */ + _("Show the debug log from the last attempted update"), NULL }, + { "list", 'l', 0, G_OPTION_ARG_NONE, &action_list, + /* TRANSLATORS: command line option */ + _("List supported firmware updates"), NULL }, + { "supported", 's', 0, G_OPTION_ARG_NONE, &action_supported, + /* TRANSLATORS: command line option */ + _("Query for firmware update support"), NULL }, + { "info", 'i', 0, G_OPTION_ARG_NONE, &action_info, + /* TRANSLATORS: command line option */ + _("Show the information of firmware update status"), NULL }, + { "enable", 'e', 0, G_OPTION_ARG_NONE, &action_enable, + /* TRANSLATORS: command line option */ + _("Enable firmware update support on supported systems"), NULL }, + { "esp-path", 'p', 0, G_OPTION_ARG_STRING, &esp_path, + /* TRANSLATORS: command line option */ + _("Override the default ESP path"), "PATH" }, + { "set-debug", 'd', 0, G_OPTION_ARG_NONE, &action_set_debug, + /* TRANSLATORS: command line option */ + _("Set the debugging flag during update"), NULL }, + { "unset-debug", 'D', 0, G_OPTION_ARG_NONE, &action_unset_debug, + /* TRANSLATORS: command line option */ + _("Unset the debugging flag during update"), NULL }, + { "apply", 'a', 0, G_OPTION_ARG_STRING, &apply, + /* TRANSLATORS: command line option */ + _("Apply firmware updates"), "GUID" }, + { NULL} + }; + + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + /* ensure root user */ + if (getuid () != 0 || geteuid () != 0) + /* TRANSLATORS: we're poking around as a power user */ + g_printerr ("%s\n", _("This program may only work correctly as root")); + + /* get a action_list of the commands */ + priv->context = g_option_context_new (NULL); + g_option_context_set_description (priv->context, + "This tool allows an administrator to debug UpdateCapsule operation."); + + /* TRANSLATORS: program name */ + g_set_application_name (_("UEFI Firmware Utility")); + g_option_context_add_main_entries (priv->context, options, NULL); + ret = g_option_context_parse (priv->context, &argc, &argv, &error); + if (!ret) { + /* TRANSLATORS: the user didn't read the man page */ + g_print ("%s: %s\n", _("Failed to parse arguments"), + error->message); + return EXIT_FAILURE; + } + + /* set verbose? */ + if (verbose) { + g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); + } else { + g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + fu_util_ignore_cb, NULL); + } + + /* nothing specified */ + if (!action_enable && !action_info && !action_list && !action_log && + !action_set_debug && !action_supported && !action_unset_debug && + !action_version && apply == NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_option_context_get_help (priv->context, TRUE, NULL); + g_printerr ("%s\n\n%s", _("No action specified!"), tmp); + return EXIT_FAILURE; + } + + /* action_version first */ + if (action_version) + g_print ("fwupd version: %s\n", PACKAGE_VERSION); + + /* override the default ESP path */ + if (esp_path != NULL) { + if (!fu_uefi_check_esp_path (esp_path, &error)) { + /* TRANSLATORS: ESP is EFI System Partition */ + g_print ("%s: %s\n", _("ESP specified was not valid"), + error->message); + return EXIT_FAILURE; + } + } else { + esp_path = fu_uefi_guess_esp_path (); + if (esp_path == NULL) { + g_printerr ("Unable to determine EFI system partition " + "location, override using --esp-path\n"); + return EXIT_FAILURE; + } + } + + /* check free space */ + if (!fu_uefi_check_esp_free_space (esp_path, + FU_UEFI_COMMON_REQUIRED_ESP_FREE_SPACE, + &error)) { + g_printerr ("Unable to use EFI system partition: %s\n", error->message); + return EXIT_FAILURE; + } + g_debug ("ESP mountpoint set as %s", esp_path); + + /* show the debug action_log from the last attempted update */ + if (action_log) { + gsize sz = 0; + g_autofree guint8 *buf = NULL; + g_autofree guint16 *buf_ucs2 = NULL; + g_autofree gchar *str = NULL; + g_autoptr(GError) error_local = NULL; + if (!fu_uefi_vars_get_data (FU_UEFI_VARS_GUID_FWUPDATE, + "FWUPDATE_DEBUG_LOG", + &buf, &sz, NULL, + &error_local)) { + g_printerr ("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + buf_ucs2 = g_new0 (guint16, (sz / 2) + 1); + memcpy (buf_ucs2, buf, sz); + str = fu_ucs2_to_uft8 (buf_ucs2, sz / 2); + g_print ("%s", str); + } + + if (action_list || action_supported || action_info) { + g_autoptr(GPtrArray) entries = NULL; + g_autofree gchar *esrt_path = NULL; + g_autofree gchar *sysfsfwdir = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the directory of ESRT entries */ + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); + entries = fu_uefi_get_esrt_entry_paths (esrt_path, &error_local); + if (entries == NULL) { + g_printerr ("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + + /* add each device */ + 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(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)); + } + } + + /* action_list action_supported firmware updates */ + if (action_list) { + for (guint i = 0; i < devices->len; i++) { + FuUefiDevice *dev = g_ptr_array_index (devices, i); + g_print ("%s type, {%s} action_version %" G_GUINT32_FORMAT " can be updated " + "to any action_version above %" G_GUINT32_FORMAT "\n", + fu_uefi_device_kind_to_string (fu_uefi_device_get_kind (dev)), + fu_uefi_device_get_guid (dev), + fu_uefi_device_get_version (dev), + fu_uefi_device_get_version_lowest (dev) - 1); + } + } + + /* query for firmware update support */ + if (action_supported) { + if (devices->len > 0) { + g_print ("%s\n", _("Firmware updates are supported on this machine.")); + } else { + g_print ("%s\n", _("Firmware updates are not supported on this machine.")); + } + } + + /* show the information of firmware update status */ + if (action_info) { + for (guint i = 0; i < devices->len; i++) { + FuUefiDevice *dev = g_ptr_array_index (devices, i); + g_autoptr(FuUefiUpdateInfo) info = NULL; + g_autoptr(GError) error_local = NULL; + + /* load any existing update info */ + info = fu_uefi_device_load_update_info (dev, &error_local); + if (info == NULL) { + g_printerr ("failed: %s\n", error_local->message); + continue; + } + g_print ("Information for the update status entry %u:\n", i); + g_print (" Information Version: %" G_GUINT32_FORMAT "\n", + fu_uefi_update_info_get_version (info)); + g_print (" Firmware GUID: {%s}\n", + fu_uefi_update_info_get_guid (info)); + g_print (" Capsule Flags: 0x%08" G_GUINT32_FORMAT "x\n", + fu_uefi_update_info_get_capsule_flags (info)); + g_print (" Hardware Instance: %" G_GUINT64_FORMAT "\n", + fu_uefi_update_info_get_hw_inst (info)); + g_print (" Update Status: %s\n", + fu_uefi_update_info_status_to_string (fu_uefi_update_info_get_status (info))); + g_print (" Capsule File Path: %s\n\n", + fu_uefi_update_info_get_capsule_fn (info)); + } + } + + /* action_enable firmware update support on action_supported systems */ + if (action_enable) { + g_printerr ("Unsupported, use `fwupdmgr unlock`\n"); + return EXIT_FAILURE; + } + + /* set the debugging flag during update */ + if (action_set_debug) { + const guint8 data = 1; + g_autoptr(GError) error_local = NULL; + if (!fu_uefi_vars_set_data (FU_UEFI_VARS_GUID_FWUPDATE, + "FWUPDATE_VERBOSE", + &data, sizeof(data), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &error_local)) { + g_printerr ("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + g_print ("%s\n", _("Enabled fwupdate debugging")); + } + + /* unset the debugging flag during update */ + if (action_unset_debug) { + g_autoptr(GError) error_local = NULL; + if (!fu_uefi_vars_delete (FU_UEFI_VARS_GUID_FWUPDATE, + "FWUPDATE_VERBOSE", + &error_local)) { + g_printerr ("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + g_print ("%s\n", _("Disabled fwupdate debugging")); + } + + /* apply firmware updates */ + if (apply != NULL) { + g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_guid (apply); + g_autoptr(GError) error_local = NULL; + g_autoptr(GBytes) fw = fu_common_get_contents_bytes (argv[1], &error_local); + if (fw == NULL) { + g_printerr ("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + fu_device_set_metadata (FU_DEVICE (dev), "EspPath", esp_path); + if (!fu_device_write_firmware (FU_DEVICE (dev), fw, + FWUPD_INSTALL_FLAG_NONE, + &error_local)) { + g_printerr ("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + } + + /* success */ + return EXIT_SUCCESS; +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-update-info.c fwupd-1.2.10/plugins/uefi/fu-uefi-update-info.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-update-info.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-update-info.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" +#include "fu-uefi-devpath.h" +#include "fu-uefi-update-info.h" +#include "fu-uefi-common.h" +#include "fu-ucs2.h" + +#include "fwupd-error.h" + +struct _FuUefiUpdateInfo { + GObject parent_instance; + guint32 version; + gchar *guid; + gchar *capsule_fn; + guint32 capsule_flags; + guint64 hw_inst; + FuUefiUpdateInfoStatus status; +}; + +G_DEFINE_TYPE (FuUefiUpdateInfo, fu_uefi_update_info, G_TYPE_OBJECT) + +const gchar * +fu_uefi_update_info_status_to_string (FuUefiUpdateInfoStatus status) +{ + if (status == FU_UEFI_UPDATE_INFO_STATUS_ATTEMPT_UPDATE) + return "attempt-update"; + if (status == FU_UEFI_UPDATE_INFO_STATUS_ATTEMPTED) + return "attempted"; + return "unknown"; +} + +static gchar * +fu_uefi_update_info_parse_dp (const guint8 *buf, gsize sz, GError **error) +{ + GBytes *dp_data; + const gchar *data; + gsize ucs2sz = 0; + g_autofree gchar *relpath = NULL; + g_autofree guint16 *ucs2file = NULL; + g_autoptr(GPtrArray) dps = NULL; + + g_return_val_if_fail (buf != NULL, NULL); + g_return_val_if_fail (sz != 0, NULL); + + /* get all headers */ + dps = fu_uefi_devpath_parse (buf, sz, FU_UEFI_DEVPATH_PARSE_FLAG_REPAIR, error); + if (dps == NULL) + return NULL; + dp_data = fu_uefi_devpath_find_data (dps, + EFIDP_MEDIA_TYPE, + EFIDP_MEDIA_FILE, + error); + if (dp_data == NULL) + return NULL; + + /* convert to UTF-8 */ + data = g_bytes_get_data (dp_data, &ucs2sz); + ucs2file = g_new0 (guint16, (ucs2sz / 2) + 1); + memcpy (ucs2file, data, ucs2sz); + relpath = fu_ucs2_to_uft8 (ucs2file, ucs2sz / sizeof (guint16)); + if (relpath == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot convert to UTF-8"); + return NULL; + } + g_strdelimit (relpath, "\\", '/'); + return g_steal_pointer (&relpath); +} + +gboolean +fu_uefi_update_info_parse (FuUefiUpdateInfo *self, const guint8 *buf, gsize sz, GError **error) +{ + efi_update_info_t info; + efi_guid_t guid_tmp; + + g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), FALSE); + + if (sz < sizeof(efi_update_info_t)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "EFI variable is corrupt"); + return FALSE; + } + memcpy (&info, buf, sizeof(info)); + self->version = info.update_info_version; + self->capsule_flags = info.capsule_flags; + self->hw_inst = info.hw_inst; + self->status = info.status; + memcpy (&guid_tmp, &info.guid, sizeof(efi_guid_t)); + if (efi_guid_to_str (&guid_tmp, &self->guid) < 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to convert GUID"); + return FALSE; + } + if (sz > sizeof(efi_update_info_t)) { + self->capsule_fn = fu_uefi_update_info_parse_dp (buf + sizeof(efi_update_info_t), + sz - sizeof(efi_update_info_t), + error); + if (self->capsule_fn == NULL) + return FALSE; + } + return TRUE; +} + +const gchar * +fu_uefi_update_info_get_guid (FuUefiUpdateInfo *self) +{ + g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), NULL); + return self->guid; +} + +const gchar * +fu_uefi_update_info_get_capsule_fn (FuUefiUpdateInfo *self) +{ + g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), NULL); + return self->capsule_fn; +} + +guint32 +fu_uefi_update_info_get_version (FuUefiUpdateInfo *self) +{ + g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0); + return self->version; +} + +guint32 +fu_uefi_update_info_get_capsule_flags (FuUefiUpdateInfo *self) +{ + g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0); + return self->capsule_flags; +} + +guint64 +fu_uefi_update_info_get_hw_inst (FuUefiUpdateInfo *self) +{ + g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0); + return self->hw_inst; +} + +FuUefiUpdateInfoStatus +fu_uefi_update_info_get_status (FuUefiUpdateInfo *self) +{ + g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), 0); + return self->status; +} + +static void +fu_uefi_update_info_finalize (GObject *object) +{ + FuUefiUpdateInfo *self = FU_UEFI_UPDATE_INFO (object); + g_free (self->guid); + g_free (self->capsule_fn); + G_OBJECT_CLASS (fu_uefi_update_info_parent_class)->finalize (object); +} + +static void +fu_uefi_update_info_class_init (FuUefiUpdateInfoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_uefi_update_info_finalize; +} + +static void +fu_uefi_update_info_init (FuUefiUpdateInfo *self) +{ +} + +FuUefiUpdateInfo * +fu_uefi_update_info_new (void) +{ + FuUefiUpdateInfo *self; + self = g_object_new (FU_TYPE_UEFI_UPDATE_INFO, NULL); + return FU_UEFI_UPDATE_INFO (self); +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-update-info.h fwupd-1.2.10/plugins/uefi/fu-uefi-update-info.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-update-info.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-update-info.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +G_BEGIN_DECLS + +#define FU_TYPE_UEFI_UPDATE_INFO (fu_uefi_update_info_get_type ()) +G_DECLARE_FINAL_TYPE (FuUefiUpdateInfo, fu_uefi_update_info, FU, UEFI_UPDATE_INFO, GObject) + +typedef enum { + FU_UEFI_UPDATE_INFO_STATUS_ATTEMPT_UPDATE = 0x00000001, + FU_UEFI_UPDATE_INFO_STATUS_ATTEMPTED = 0x00000002, +} FuUefiUpdateInfoStatus; + +const gchar *fu_uefi_update_info_status_to_string (FuUefiUpdateInfoStatus status); + +FuUefiUpdateInfo *fu_uefi_update_info_new (void); +gboolean fu_uefi_update_info_parse (FuUefiUpdateInfo *self, + const guint8 *buf, + gsize sz, + GError **error); +guint32 fu_uefi_update_info_get_version (FuUefiUpdateInfo *self); +const gchar *fu_uefi_update_info_get_guid (FuUefiUpdateInfo *self); +const gchar *fu_uefi_update_info_get_capsule_fn (FuUefiUpdateInfo *self); +guint32 fu_uefi_update_info_get_capsule_flags (FuUefiUpdateInfo *self); +guint64 fu_uefi_update_info_get_hw_inst (FuUefiUpdateInfo *self); +FuUefiUpdateInfoStatus fu_uefi_update_info_get_status (FuUefiUpdateInfo *self); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-vars.c fwupd-1.2.10/plugins/uefi/fu-uefi-vars.c --- fwupd-1.0.9/plugins/uefi/fu-uefi-vars.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-vars.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fu-common.h" +#include "fu-uefi-vars.h" + +#include "fwupd-error.h" + +static gchar * +fu_uefi_vars_get_path (void) +{ + g_autofree gchar *sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + return g_build_filename (sysfsfwdir, "efi", "efivars", NULL); +} + +static gchar * +fu_uefi_vars_get_filename (const gchar *guid, const gchar *name) +{ + g_autofree gchar *efivardir = fu_uefi_vars_get_path (); + return g_strdup_printf ("%s/%s-%s", efivardir, name, guid); +} + +gboolean +fu_uefi_vars_supported (GError **error) +{ + g_autofree gchar *efivardir = fu_uefi_vars_get_path (); + if (!g_file_test (efivardir, G_FILE_TEST_IS_DIR)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "kernel efivars support missing: %s", + efivardir); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_uefi_vars_set_immutable_fd (int fd, + gboolean value, + gboolean *value_old, + GError **error) +{ + guint flags; + gboolean is_immutable; + int rc; + + /* get existing status */ + rc = ioctl (fd, FS_IOC_GETFLAGS, &flags); + if (rc < 0) { + /* check for tmpfs */ + if (errno == ENOTTY || errno == ENOSYS) { + is_immutable = FALSE; + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to get flags: %s", + strerror (errno)); + return FALSE; + } + } else { + is_immutable = (flags & FS_IMMUTABLE_FL) > 0; + } + + /* save the old value */ + if (value_old != NULL) + *value_old = is_immutable; + + /* is this already correct */ + if (value) { + if (is_immutable) + return TRUE; + flags |= FS_IMMUTABLE_FL; + } else { + if (!is_immutable) + return TRUE; + flags &= ~FS_IMMUTABLE_FL; + } + + /* set the new status */ + rc = ioctl (fd, FS_IOC_SETFLAGS, &flags); + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to set flags: %s", + strerror (errno)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_uefi_vars_set_immutable (const gchar *fn, + gboolean value, + gboolean *value_old, + GError **error) +{ + gint fd; + g_autoptr(GInputStream) istr = NULL; + + /* open file readonly */ + fd = open (fn, O_RDONLY); + if (fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_FILENAME, + "failed to open: %s", + strerror (errno)); + return FALSE; + } + istr = g_unix_input_stream_new (fd, TRUE); + return fu_uefi_vars_set_immutable_fd (fd, value, value_old, error); +} + +gboolean +fu_uefi_vars_delete (const gchar *guid, const gchar *name, GError **error) +{ + g_autofree gchar *fn = fu_uefi_vars_get_filename (guid, name); + g_autoptr(GFile) file = g_file_new_for_path (fn); + if (!g_file_query_exists (file, NULL)) + return TRUE; + if (!fu_uefi_vars_set_immutable (fn, FALSE, NULL, error)) { + g_prefix_error (error, "failed to set %s as mutable: ", fn); + return FALSE; + } + return g_file_delete (file, NULL, error); +} + +gboolean +fu_uefi_vars_delete_with_glob (const gchar *guid, const gchar *name_glob, GError **error) +{ + const gchar *fn; + g_autofree gchar *nameguid_glob = NULL; + g_autofree gchar *efivardir = fu_uefi_vars_get_path (); + g_autoptr(GDir) dir = g_dir_open (efivardir, 0, error); + if (dir == NULL) + return FALSE; + nameguid_glob = g_strdup_printf ("%s-%s", name_glob, guid); + while ((fn = g_dir_read_name (dir)) != NULL) { + if (fnmatch (nameguid_glob, fn, 0) == 0) { + g_autofree gchar *keyfn = g_build_filename (efivardir, fn, NULL); + g_autoptr(GFile) file = g_file_new_for_path (keyfn); + if (!fu_uefi_vars_set_immutable (keyfn, FALSE, NULL, error)) { + g_prefix_error (error, "failed to set %s as mutable: ", keyfn); + return FALSE; + } + if (!g_file_delete (file, NULL, error)) + return FALSE; + } + } + return TRUE; +} + +gboolean +fu_uefi_vars_exists (const gchar *guid, const gchar *name) +{ + g_autofree gchar *fn = fu_uefi_vars_get_filename (guid, name); + return g_file_test (fn, G_FILE_TEST_EXISTS); +} + +gboolean +fu_uefi_vars_get_data (const gchar *guid, const gchar *name, guint8 **data, + gsize *data_sz, guint32 *attr, GError **error) +{ + gssize attr_sz; + gssize data_sz_tmp; + guint32 attr_tmp; + guint64 sz; + g_autofree gchar *fn = fu_uefi_vars_get_filename (guid, name); + g_autoptr(GFile) file = g_file_new_for_path (fn); + g_autoptr(GFileInfo) info = NULL; + g_autoptr(GInputStream) istr = NULL; + + /* open file as stream */ + istr = G_INPUT_STREAM (g_file_read (file, NULL, error)); + if (istr == NULL) + return FALSE; + info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (istr), + G_FILE_ATTRIBUTE_STANDARD_SIZE, + NULL, error); + if (info == NULL) { + g_prefix_error (error, "failed to get stream info: "); + return FALSE; + } + + /* get total stream size */ + sz = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE); + if (sz < 4) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "efivars file too small: %" G_GUINT64_FORMAT, sz); + return FALSE; + } + + /* read out the attributes */ + attr_sz = g_input_stream_read (istr, &attr_tmp, sizeof(attr_tmp), NULL, error); + if (attr_sz == -1) { + g_prefix_error (error, "failed to read attr: "); + return FALSE; + } + if (attr != NULL) + *attr = attr_tmp; + + /* read out the data */ + data_sz_tmp = sz - sizeof(attr_tmp); + if (data_sz != NULL) + *data_sz = data_sz_tmp; + if (data != NULL) { + g_autofree guint8 *data_tmp = g_malloc0 (data_sz_tmp); + if (!g_input_stream_read_all (istr, data_tmp, data_sz_tmp, + NULL, NULL, error)) { + g_prefix_error (error, "failed to read data: "); + return FALSE; + } + *data = g_steal_pointer (&data_tmp); + } + return TRUE; +} + +gboolean +fu_uefi_vars_set_data (const gchar *guid, const gchar *name, const guint8 *data, + gsize data_sz, guint32 attr, GError **error) +{ + int fd; + gboolean was_immutable; + g_autofree gchar *fn = fu_uefi_vars_get_filename (guid, name); + g_autofree guint8 *buf = g_malloc0 (sizeof(guint32) + data_sz); + g_autoptr(GFile) file = g_file_new_for_path (fn); + g_autoptr(GOutputStream) ostr = NULL; + + /* create empty file so we can clear the immutable bit before writing */ + if (!g_file_query_exists (file, NULL)) { + g_autoptr(GFileOutputStream) ostr_tmp = NULL; + ostr_tmp = g_file_create (file, + G_FILE_CREATE_NONE, + NULL, + error); + if (ostr_tmp == NULL) + return FALSE; + if (!g_output_stream_close (G_OUTPUT_STREAM (ostr_tmp), NULL, error)) + return FALSE; + } + if (!fu_uefi_vars_set_immutable (fn, FALSE, &was_immutable, error)) { + g_prefix_error (error, "failed to set %s as mutable: ", fn); + return FALSE; + } + + /* open file for writing */ + fd = open (fn, O_WRONLY); + if (fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to open %s: %s", + fn, strerror (errno)); + return FALSE; + } + ostr = g_unix_output_stream_new (fd, TRUE); + memcpy (buf, &attr, sizeof(attr)); + memcpy (buf + sizeof(attr), data, data_sz); + if (g_output_stream_write (ostr, buf, sizeof(attr) + data_sz, NULL, error) < 0) + return FALSE; + + /* set as immutable again */ + if (was_immutable && !fu_uefi_vars_set_immutable (fn, TRUE, NULL, error)) { + g_prefix_error (error, "failed to set %s as immutable: ", fn); + return FALSE; + } + + /* success */ + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/uefi/fu-uefi-vars.h fwupd-1.2.10/plugins/uefi/fu-uefi-vars.h --- fwupd-1.0.9/plugins/uefi/fu-uefi-vars.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/fu-uefi-vars.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015-2017 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define FU_UEFI_VARS_GUID_EFI_GLOBAL "8be4df61-93ca-11d2-aa0d-00e098032b8c" +#define FU_UEFI_VARS_GUID_FWUPDATE "0abba7dc-e516-4167-bbf5-4d9d1c739416" +#define FU_UEFI_VARS_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" + +#define FU_UEFI_VARS_ATTR_NON_VOLATILE (1 << 0) +#define FU_UEFI_VARS_ATTR_BOOTSERVICE_ACCESS (1 << 1) +#define FU_UEFI_VARS_ATTR_RUNTIME_ACCESS (1 << 2) +#define FU_UEFI_VARS_ATTR_HARDWARE_ERROR_RECORD (1 << 3) +#define FU_UEFI_VARS_ATTR_AUTHENTICATED_WRITE_ACCESS (1 << 4) +#define FU_UEFI_VARS_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS (5 << 0) +#define FU_UEFI_VARS_ATTR_APPEND_WRITE (1 << 6) + +gboolean fu_uefi_vars_supported (GError **error); +gboolean fu_uefi_vars_exists (const gchar *guid, + const gchar *name); +gboolean fu_uefi_vars_get_data (const gchar *guid, + const gchar *name, + guint8 **data, + gsize *data_sz, + guint32 *attr, + GError **error); +gboolean fu_uefi_vars_set_data (const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error); +gboolean fu_uefi_vars_delete (const gchar *guid, + const gchar *name, + GError **error); +gboolean fu_uefi_vars_delete_with_glob (const gchar *guid, + const gchar *name_glob, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/uefi/meson.build fwupd-1.2.10/plugins/uefi/meson.build --- fwupd-1.0.9/plugins/uefi/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,8 +1,29 @@ +subdir('efi') + cargs = ['-DG_LOG_DOMAIN="FuPluginUefi"'] +efi_os_dir = get_option('efi_os_dir') +if efi_os_dir != '' + cargs += '-DEFI_OS_DIR="' + efi_os_dir + '"' +endif + +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', + 'fu-ucs2.c', + 'fu-uefi-bootmgr.c', + 'fu-uefi-common.c', + 'fu-uefi-device.c', + 'fu-uefi-devpath.c', + 'fu-uefi-pcrs.c', + 'fu-uefi-update-info.c', + 'fu-uefi-vars.c', ], include_directories : [ include_directories('../..'), @@ -11,22 +32,90 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, - fwup, + efivar, + efiboot, ], ) -con2 = configuration_data() -con2.set('bootdir', get_option('bootdir')) +executable( + 'fwupdate', + resources_src, + fu_hash, + sources : [ + 'fu-uefi-tool.c', + 'fu-uefi-bgrt.c', + 'fu-ucs2.c', + 'fu-uefi-bootmgr.c', + 'fu-uefi-common.c', + 'fu-uefi-device.c', + 'fu-uefi-devpath.c', + 'fu-uefi-pcrs.c', + 'fu-uefi-update-info.c', + 'fu-uefi-vars.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + dependencies : [ + libxmlb, + giounix, + gusb, + gudev, + efivar, + efiboot, + ], + link_with : [ + libfwupdprivate, + ], + install : true, + install_dir : join_paths(libexecdir, 'fwupd'), + c_args : cargs, +) -# replace @bootdir@ -configure_file( - input : 'uefi.conf.in', - output : 'uefi.conf', - configuration : con2, - install: true, - install_dir : join_paths(sysconfdir, 'fwupd') +install_data(['uefi.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') ) +if get_option('tests') + testdatadir = join_paths(meson.current_source_dir(), 'tests') + cargs += '-DTESTDATADIR="' + testdatadir + '"' + e = executable( + 'uefi-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-uefi-bgrt.c', + 'fu-uefi-bootmgr.c', + 'fu-uefi-common.c', + 'fu-uefi-device.c', + 'fu-uefi-devpath.c', + 'fu-uefi-pcrs.c', + 'fu-uefi-update-info.c', + 'fu-uefi-vars.c', + 'fu-ucs2.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + dependencies : [ + plugin_deps, + efivar, + efiboot, + ], + link_with : [ + libfwupdprivate, + ], + c_args : cargs + ) + test('uefi-self-test', e) +endif diff -Nru fwupd-1.0.9/plugins/uefi/README.md fwupd-1.2.10/plugins/uefi/README.md --- fwupd-1.0.9/plugins/uefi/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -8,38 +8,46 @@ defines the software interface between an OS and platform firmware. With the UpdateCapsule boot service it can be used to update system firmware. -Build Requirements ------------------- +If you don't want or need this functionality you can use the +`-Dplugin_uefi=false` option. -For UEFI capsule support, you need to install fwupdate 0.5 or later. -* source: https://github.com/rhinstaller/fwupdate -* rpms: https://pjones.fedorapeople.org/fwupdate/ -* debs (Debian): https://tracker.debian.org/pkg/fwupdate -* debs (Ubuntu): https://launchpad.net/ubuntu/+source/fwupdate +Firmware Format +--------------- -If you don't want or need this functionality you can use the -`--disable-uefi` option. +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 ------------------- On some Dell systems it is possible to turn on and off UEFI capsule support from within the BIOS. This functionality can also be adjusted -from within the OS by fwupd. This requires using fwupdate 5 or later -and compiling it with libsmbios support. - -When fwupd and fwupdate have been compiled with this support you will -be able to enable UEFI support on the device by using the `unlock` command. +from within the OS by fwupd. This requires compiling with libsmbios support. -Custom EFI System Partition location ---------------------- -`fwupdate` 10 and later allow using an EFI system partition location -at runtime that is different than the location compiled into the library. +When fwupd has been compiled with this support you will be able to enable UEFI +support on the device by using the `unlock` command. -fwupd 1.0.6 and later can take advantage of this feature by allowing -users to modify `/etc/fwupd/uefi.conf`. +Custom EFI System Partition +--------------------------- -An option titled *OverrideESPMountPoint* is available that can be -uncommented and set to any valid directory on the system. +Since version 1.1.0 fwupd will autodetect the ESP when it is mounted on +`/boot/efi`, `/boot`, or `/efi`. A custom EFI system partition location can be +used by modifying *OverrideESPMountPoint* in `/etc/fwupd/uefi.conf`. Setting an invalid directory will disable the fwupd plugin. Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/image and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/image differ diff -Nru fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/status fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/status --- fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/type fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/type --- fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/version fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/version --- fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/xoffset fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/xoffset --- fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/xoffset 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/xoffset 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +123 diff -Nru fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/yoffset fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/yoffset --- fwupd-1.0.9/plugins/uefi/tests/acpi/bgrt/yoffset 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/acpi/bgrt/yoffset 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +456 Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/uefi/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/uefi/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 differ diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c fwupd-1.2.10/plugins/uefi/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c --- fwupd-1.0.9/plugins/uefi/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 \ No newline at end of file diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/capsule_flags fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/capsule_flags --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/capsule_flags 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/capsule_flags 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0xfe diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/fw_class fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/fw_class --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/fw_class 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/fw_class 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +ddc0ee61-e7f0-4e7d-acc5-c070a398838e diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/fw_type fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/fw_type --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/fw_type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/fw_type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/fw_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/fw_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +65586 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_status fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_status --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/last_attempt_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +18472960 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/lowest_supported_fw_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/lowest_supported_fw_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry0/lowest_supported_fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry0/lowest_supported_fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +65582 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/capsule_flags fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/capsule_flags --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/capsule_flags 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/capsule_flags 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0x8010 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/fw_class fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/fw_class --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/fw_class 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/fw_class 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +671d19d0-d43c-4852-98d9-1ce16f9967e4 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/fw_type fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/fw_type --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/fw_type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/fw_type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +2 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/fw_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/fw_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +3090287969 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_status fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_status --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/last_attempt_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/lowest_supported_fw_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/lowest_supported_fw_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry1/lowest_supported_fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry1/lowest_supported_fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/capsule_flags fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/capsule_flags --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/capsule_flags 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/capsule_flags 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0x8010 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/fw_class fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/fw_class --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/fw_class 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/fw_class 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +00000000-0000-0000-0000-000000000000 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/fw_type fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/fw_type --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/fw_type 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/fw_type 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +2 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/fw_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/fw_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +3090287969 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_status fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_status --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_status 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_status 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/last_attempt_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +0 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/lowest_supported_fw_version fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/lowest_supported_fw_version --- fwupd-1.0.9/plugins/uefi/tests/efi/esrt/entries/entry2/lowest_supported_fw_version 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/esrt/entries/entry2/lowest_supported_fw_version 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi/fw_platform_size fwupd-1.2.10/plugins/uefi/tests/efi/fw_platform_size --- fwupd-1.0.9/plugins/uefi/tests/efi/fw_platform_size 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi/fw_platform_size 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +64 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/height fwupd-1.2.10/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/height --- fwupd-1.0.9/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/height 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/height 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +789 diff -Nru fwupd-1.0.9/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/width fwupd-1.2.10/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/width --- fwupd-1.0.9/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/width 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/efi-framebuffer/efi-framebuffer.0/width 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +456 diff -Nru fwupd-1.0.9/plugins/uefi/tests/.gitignore fwupd-1.2.10/plugins/uefi/tests/.gitignore --- fwupd-1.0.9/plugins/uefi/tests/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/.gitignore 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +EFI +efi/efivars/fwupd-c34cb672-a81e-5d32-9d89-cbcabe8ec37b-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 Binary files /tmp/tmpzTO9iQ/BvsexTgW9H/fwupd-1.0.9/plugins/uefi/tests/test.bmp and /tmp/tmpzTO9iQ/nurRHvTZu3/fwupd-1.2.10/plugins/uefi/tests/test.bmp differ diff -Nru fwupd-1.0.9/plugins/uefi/tests/tpm0/active fwupd-1.2.10/plugins/uefi/tests/tpm0/active --- fwupd-1.0.9/plugins/uefi/tests/tpm0/active 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/tpm0/active 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/tpm0/caps fwupd-1.2.10/plugins/uefi/tests/tpm0/caps --- fwupd-1.0.9/plugins/uefi/tests/tpm0/caps 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/tpm0/caps 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,3 @@ +Manufacturer: 0x49465800 +TCG version: 1.2 +Firmware version: 6.40 diff -Nru fwupd-1.0.9/plugins/uefi/tests/tpm0/enabled fwupd-1.2.10/plugins/uefi/tests/tpm0/enabled --- fwupd-1.0.9/plugins/uefi/tests/tpm0/enabled 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/tpm0/enabled 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/tpm0/owned fwupd-1.2.10/plugins/uefi/tests/tpm0/owned --- fwupd-1.0.9/plugins/uefi/tests/tpm0/owned 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/tpm0/owned 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +1 diff -Nru fwupd-1.0.9/plugins/uefi/tests/tpm0/pcrs fwupd-1.2.10/plugins/uefi/tests/tpm0/pcrs --- fwupd-1.0.9/plugins/uefi/tests/tpm0/pcrs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/tests/tpm0/pcrs 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/uefi/uefi.conf fwupd-1.2.10/plugins/uefi/uefi.conf --- fwupd-1.0.9/plugins/uefi/uefi.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/uefi.conf 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,12 @@ +[uefi] + +# the shim loader is required to chainload the fwupd EFI binary unless +# the fwupd.efi file has been self-signed manually +RequireShimForSecureBoot=true + +# the EFI system partition path used +# if this is is not /boot/efi, /boot, or /efi +#OverrideESPMountPoint= + +# amount of free space required on the ESP, for example using 0x2000000 for 32Mb +#RequireESPFreeSpace= diff -Nru fwupd-1.0.9/plugins/uefi/uefi.conf.in fwupd-1.2.10/plugins/uefi/uefi.conf.in --- fwupd-1.0.9/plugins/uefi/uefi.conf.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/uefi.conf.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -[uefi] - -# For fwupdate 10+ allow overriding -# the compiled EFI system partition path -#OverrideESPMountPoint=@bootdir@ diff -Nru fwupd-1.0.9/plugins/uefi/uefi.quirk fwupd-1.2.10/plugins/uefi/uefi.quirk --- fwupd-1.0.9/plugins/uefi/uefi.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/uefi/uefi.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,22 @@ +# 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 + +# Star Labs LabTop (Mk III) +[Guid=8265d473-a6c2-42b4-897b-bc220faa2d32] +Flags = use-shim-unique + +# Star Labs Lite (Mk II) +[Guid=797f8bae-0ea2-4c0f-8a30-7d10ccfacbc0] +Flags = use-shim-unique,no-ux-capsule diff -Nru fwupd-1.0.9/plugins/unifying/fu-plugin-unifying.c fwupd-1.2.10/plugins/unifying/fu-plugin-unifying.c --- fwupd-1.0.9/plugins/unifying/fu-plugin-unifying.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-plugin-unifying.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,218 +1,172 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016 Richard Hughes +/* + * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" -#include #include -#include -#include "fu-plugin.h" #include "fu-plugin-vfuncs.h" -#include "lu-context.h" -#include "lu-device.h" -#include "lu-device-peripheral.h" - -struct FuPluginData { - LuContext *ctx; -}; +#include "fu-unifying-bootloader-nordic.h" +#include "fu-unifying-bootloader-texas.h" +#include "fu-unifying-common.h" +#include "fu-unifying-peripheral.h" +#include "fu-unifying-runtime.h" -static gboolean -fu_plugin_unifying_device_added (FuPlugin *plugin, - LuDevice *device, - GError **error) +gboolean +fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) { - g_autoptr(AsProfile) profile = as_profile_new (); - g_autoptr(AsProfileTask) ptask = NULL; - - /* profile */ - ptask = as_profile_start (profile, "FuPluginLu:added{%s}", - fu_device_get_platform_id (FU_DEVICE (device))); - g_assert (ptask != NULL); - - /* open the device */ - if (!lu_device_open (device, 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; - - /* insert to hash */ - fu_plugin_device_add (plugin, FU_DEVICE (device)); - return TRUE; + return fu_device_detach (device, error); } -static gboolean -fu_plugin_unifying_detach_cb (gpointer user_data) +gboolean +fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) { - FuDevice *device = FU_DEVICE (user_data); - g_autoptr(GError) error = NULL; - - /* ditch this device */ - g_debug ("detaching"); - if (!fu_device_detach (device, &error)) { - g_warning ("failed to detach: %s", error->message); + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) return FALSE; - } - - return FALSE; + return fu_device_attach (device, error); } -static gboolean -fu_plugin_unifying_attach_cb (gpointer user_data) +gboolean +fu_plugin_update_reload (FuPlugin *plugin, FuDevice *device, GError **error) { - FuDevice *device = FU_DEVICE (user_data); - g_autoptr(GError) error = NULL; - - /* ditch this device */ - g_debug ("attaching"); - if (!fu_device_attach (device, &error)) { - g_warning ("failed to detach: %s", error->message); + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) return FALSE; - } - - return FALSE; + return TRUE; } gboolean -fu_plugin_update_detach (FuPlugin *plugin, FuDevice *dev, GError **error) +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - LuDevice *device = LU_DEVICE (dev); - - /* get device */ - if (!lu_device_open (device, error)) + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) return FALSE; + return fu_device_write_firmware (device, blob_fw, flags, error); +} - /* switch to bootloader if required */ - if (!lu_device_has_flag (device, LU_DEVICE_FLAG_REQUIRES_DETACH)) - return TRUE; - - /* wait for device to come back */ - fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART); - if (lu_device_has_flag (device, LU_DEVICE_FLAG_DETACH_WILL_REPLUG)) { - g_debug ("doing detach in idle"); - g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, - fu_plugin_unifying_detach_cb, - g_object_ref (dev), - (GDestroyNotify) g_object_unref); - if (!lu_context_wait_for_replug (data->ctx, - device, - FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, - error)) - return FALSE; - } else { - g_debug ("doing detach in main thread"); - if (!fu_device_detach (dev, error)) - return FALSE; +static gboolean +fu_plugin_unifying_check_supported_device (FuPlugin *plugin, FuDevice *device) +{ + GPtrArray *guids = fu_device_get_guids (device); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index (guids, i); + if (fu_plugin_check_supported (plugin, guid)) + return TRUE; } - return TRUE; + return FALSE; } gboolean -fu_plugin_update_attach (FuPlugin *plugin, FuDevice *dev, GError **error) +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - LuDevice *device = LU_DEVICE (dev); + g_autoptr(FuDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; - /* get device */ - if (!lu_device_open (device, error)) - return FALSE; + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "hidraw") != 0) + return TRUE; - /* wait for it to appear back in runtime mode if required */ - if (!lu_device_has_flag (device, LU_DEVICE_FLAG_REQUIRES_ATTACH)) + /* logitech */ + if (fu_udev_device_get_vendor (device) != FU_UNIFYING_DEVICE_VID) return TRUE; - /* wait for device to come back */ - fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART); - if (lu_device_has_flag (device, LU_DEVICE_FLAG_ATTACH_WILL_REPLUG)) { - g_debug ("doing attach in idle"); - g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, - fu_plugin_unifying_attach_cb, - g_object_ref (device), - (GDestroyNotify) g_object_unref); - if (!lu_context_wait_for_replug (data->ctx, - device, - FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, - error)) - return FALSE; + /* runtime */ + if (fu_device_has_custom_flag (FU_DEVICE (device), "is-receiver")) { + dev = g_object_new (FU_TYPE_UNIFYING_RUNTIME, + "version-format", FWUPD_VERSION_FORMAT_PLAIN, + NULL); + fu_device_incorporate (dev, FU_DEVICE (device)); } else { - g_debug ("doing attach in main thread"); - if (!fu_device_attach (dev, error)) + + /* create device so we can run ->probe() and add UFY GUIDs */ + dev = g_object_new (FU_TYPE_UNIFYING_PERIPHERAL, + "version-format", FWUPD_VERSION_FORMAT_PLAIN, + NULL); + fu_device_incorporate (dev, FU_DEVICE (device)); + if (!fu_device_probe (dev, error)) return FALSE; - } - return TRUE; -} -gboolean -fu_plugin_update_reload (FuPlugin *plugin, FuDevice *dev, GError **error) -{ - LuDevice *device = LU_DEVICE (dev); + /* there are a lot of unifying peripherals, but not all respond + * well to opening -- so limit to ones with issued updates */ + if (!fu_plugin_unifying_check_supported_device (plugin, dev)) { + g_autofree gchar *guids = fu_device_get_guids_as_str (FU_DEVICE (device)); + g_debug ("%s has no updates, so ignoring device", guids); + return TRUE; + } + } - /* get device */ - if (!lu_device_open (device, error)) + /* open to get the version */ + locker = fu_device_locker_new (dev, error); + if (locker == NULL) return FALSE; + fu_plugin_device_add (plugin, dev); return TRUE; } gboolean -fu_plugin_update (FuPlugin *plugin, - FuDevice *dev, - GBytes *blob_fw, - FwupdInstallFlags flags, - GError **error) +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) { - LuDevice *device = LU_DEVICE (dev); + g_autoptr(FuDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; - /* get version */ - if (!lu_device_open (device, error)) - return FALSE; - - /* write the firmware */ - fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); - if (!fu_device_write_firmware (dev, blob_fw, error)) - return FALSE; - - /* success */ - return TRUE; -} + /* logitech */ + if (fu_usb_device_get_vid (device) != FU_UNIFYING_DEVICE_VID) + return TRUE; -static void -fu_plugin_unifying_device_added_cb (LuContext *ctx, - LuDevice *device, - FuPlugin *plugin) -{ - g_autoptr(GError) error = NULL; + /* check is bootloader */ + if (!fu_device_has_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug ("not in bootloader mode, ignoring"); + return TRUE; + } + if (fu_device_has_custom_flag (FU_DEVICE (device), "is-nordic")) { + dev = g_object_new (FU_TYPE_UNIFYING_BOOTLOADER_NORDIC, + "version-format", FWUPD_VERSION_FORMAT_PLAIN, + NULL); + fu_device_incorporate (dev, FU_DEVICE (device)); + } else if (fu_device_has_custom_flag (FU_DEVICE (device), "is-texas")) { + dev = g_object_new (FU_TYPE_UNIFYING_BOOTLOADER_TEXAS, + "version-format", FWUPD_VERSION_FORMAT_PLAIN, + NULL); + fu_device_incorporate (dev, FU_DEVICE (device)); + g_usleep (200*1000); + } - /* add */ - if (!fu_plugin_unifying_device_added (plugin, device, &error)) { - if (g_error_matches (error, + /* not supported */ + if (dev == NULL) { + g_set_error_literal (error, FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED)) { - g_debug ("Failed to add Logitech device: %s", - error->message); - } else { - g_warning ("Failed to add Logitech device: %s", - error->message); - } + FWUPD_ERROR_NOT_SUPPORTED, + "bootloader device not supported"); + return FALSE; } -} -static void -fu_plugin_unifying_device_removed_cb (LuContext *ctx, - LuDevice *device, - FuPlugin *plugin) -{ - fu_plugin_device_remove (plugin, FU_DEVICE (device)); + /* open to get the version */ + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, dev); + return TRUE; } gboolean fu_plugin_startup (FuPlugin *plugin, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - /* check the kernel has CONFIG_HIDRAW */ if (!g_file_test ("/sys/class/hidraw", G_FILE_TEST_IS_DIR)) { g_set_error_literal (error, @@ -221,39 +175,15 @@ "no kernel support for CONFIG_HIDRAW"); return FALSE; } - - /* coldplug */ - g_signal_connect (data->ctx, "added", - G_CALLBACK (fu_plugin_unifying_device_added_cb), - plugin); - g_signal_connect (data->ctx, "removed", - G_CALLBACK (fu_plugin_unifying_device_removed_cb), - plugin); - lu_context_set_supported (data->ctx, fu_plugin_get_supported (plugin)); - return TRUE; -} - - -gboolean -fu_plugin_coldplug (FuPlugin *plugin, GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - lu_context_coldplug (data->ctx); - lu_context_set_poll_interval (data->ctx, 5000); return TRUE; } void fu_plugin_init (FuPlugin *plugin) { - FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); - GUsbContext *usb_ctx = fu_plugin_get_usb_context (plugin); - data->ctx = lu_context_new_full (usb_ctx); -} - -void -fu_plugin_destroy (FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - g_object_unref (data->ctx); + 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 -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader.c fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-unifying-common.h" +#include "fu-unifying-bootloader.h" +#include "fu-unifying-hidpp.h" + +typedef struct +{ + guint16 flash_addr_lo; + guint16 flash_addr_hi; + guint16 flash_blocksize; +} FuUnifyingBootloaderPrivate; + +#define FU_UNIFYING_DEVICE_EP1 0x81 +#define FU_UNIFYING_DEVICE_EP3 0x83 + +G_DEFINE_TYPE_WITH_PRIVATE (FuUnifyingBootloader, fu_unifying_bootloader, FU_TYPE_USB_DEVICE) + +#define GET_PRIVATE(o) (fu_unifying_bootloader_get_instance_private (o)) + +static void +fu_unifying_bootloader_to_string (FuDevice *device, GString *str) +{ + FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); + FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); + g_string_append_printf (str, " FlashAddrHigh:\t0x%04x\n", + priv->flash_addr_hi); + g_string_append_printf (str, " FlashAddrLow:\t0x%04x\n", + priv->flash_addr_lo); + g_string_append_printf (str, " FlashBlockSize:\t0x%04x\n", + priv->flash_blocksize); +} + +FuUnifyingBootloaderRequest * +fu_unifying_bootloader_request_new (void) +{ + FuUnifyingBootloaderRequest *req = g_new0 (FuUnifyingBootloaderRequest, 1); + return req; +} + +GPtrArray * +fu_unifying_bootloader_parse_requests (FuUnifyingBootloader *self, GBytes *fw, GError **error) +{ + const gchar *tmp; + g_auto(GStrv) lines = NULL; + g_autoptr(GPtrArray) reqs = NULL; + guint32 last_addr = 0; + + reqs = g_ptr_array_new_with_free_func (g_free); + tmp = g_bytes_get_data (fw, NULL); + lines = g_strsplit_set (tmp, "\n\r", -1); + for (guint i = 0; lines[i] != NULL; i++) { + g_autoptr(FuUnifyingBootloaderRequest) payload = NULL; + guint8 rec_type = 0x00; + + /* skip empty lines */ + tmp = lines[i]; + if (strlen (tmp) < 5) + continue; + + payload = fu_unifying_bootloader_request_new (); + payload->len = fu_unifying_buffer_read_uint8 (tmp + 0x01); + if (payload->len > 28) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware data invalid: too large %u bytes", + payload->len); + return NULL; + } + payload->addr = ((guint16) fu_unifying_buffer_read_uint8 (tmp + 0x03)) << 8; + payload->addr |= fu_unifying_buffer_read_uint8 (tmp + 0x05); + + rec_type = fu_unifying_buffer_read_uint8 (tmp + 0x07); + + /* record type of 0xFD indicates signature data */ + if (rec_type == 0xFD) { + payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE; + } else { + payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER; + } + + /* read the data, but skip the checksum byte */ + for (guint j = 0; j < payload->len; j++) { + const gchar *ptr = tmp + 0x09 + (j * 2); + if (ptr[0] == '\0') { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware data invalid: expected %u bytes", + payload->len); + return NULL; + } + payload->data[j] = fu_unifying_buffer_read_uint8 (ptr); + } + + /* no need to bound check signature addresses */ + if (payload->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { + g_ptr_array_add (reqs, g_steal_pointer (&payload)); + continue; + } + + /* skip the bootloader */ + if (payload->addr > fu_unifying_bootloader_get_addr_hi (self)) { + g_debug ("skipping write @ %04x", payload->addr); + continue; + } + + /* skip the header */ + if (payload->addr < fu_unifying_bootloader_get_addr_lo (self)) { + g_debug ("skipping write @ %04x", payload->addr); + continue; + } + + /* make sure firmware addresses only go up */ + if (payload->addr < last_addr) { + g_debug ("skipping write @ %04x", payload->addr); + continue; + } + last_addr = payload->addr; + + /* pending */ + g_ptr_array_add (reqs, g_steal_pointer (&payload)); + } + if (reqs->len == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware data invalid: no payloads found"); + return NULL; + } + return g_steal_pointer (&reqs); +} + +guint16 +fu_unifying_bootloader_get_addr_lo (FuUnifyingBootloader *self) +{ + FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); + return priv->flash_addr_lo; +} + +guint16 +fu_unifying_bootloader_get_addr_hi (FuUnifyingBootloader *self) +{ + FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); + return priv->flash_addr_hi; +} + +guint16 +fu_unifying_bootloader_get_blocksize (FuUnifyingBootloader *self) +{ + FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UNIFYING_BOOTLOADER (self), 0x0000); + return priv->flash_blocksize; +} + +static gboolean +fu_unifying_bootloader_attach (FuDevice *device, GError **error) +{ + FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_REBOOT; + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to attach back to runtime: "); + return FALSE; + } + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_unifying_bootloader_set_bl_version (FuUnifyingBootloader *self, GError **error) +{ + guint16 build; + g_autofree gchar *version = NULL; + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + + /* call into hardware */ + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_BL_VERSION; + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to get firmware version: "); + return FALSE; + } + + /* BOTxx.yy_Bzzzz + * 012345678901234 */ + build = (guint16) fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; + build += fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 12); + version = fu_unifying_format_version ("BOT", + fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 3), + fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 6), + build); + if (version == NULL) { + g_prefix_error (error, "failed to format firmware version: "); + return FALSE; + } + fu_device_set_version_bootloader (FU_DEVICE (self), version); + return TRUE; +} + +static gboolean +fu_unifying_bootloader_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + const guint idx = 0x00; + + /* claim the only interface */ + if (!g_usb_device_claim_interface (usb_device, idx, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "Failed to claim 0x%02x: ", idx); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_unifying_bootloader_setup (FuDevice *device, GError **error) +{ + FuUnifyingBootloaderClass *klass = FU_UNIFYING_BOOTLOADER_GET_CLASS (device); + FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); + FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + + /* get memory map */ + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO; + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to get meminfo: "); + return FALSE; + } + if (req->len != 0x06) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to get meminfo: invalid size %02x", + req->len); + return FALSE; + } + + /* parse values */ + priv->flash_addr_lo = fu_common_read_uint16 (req->data + 0, G_BIG_ENDIAN); + priv->flash_addr_hi = fu_common_read_uint16 (req->data + 2, G_BIG_ENDIAN); + priv->flash_blocksize = fu_common_read_uint16 (req->data + 4, G_BIG_ENDIAN); + + /* get bootloader version */ + if (!fu_unifying_bootloader_set_bl_version (self, error)) + return FALSE; + + /* subclassed further */ + if (klass->setup != NULL) + return klass->setup (self, error); + + /* success */ + return TRUE; +} + +static gboolean +fu_unifying_bootloader_close (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + if (usb_device != NULL) { + if (!g_usb_device_release_interface (usb_device, 0x00, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + return FALSE; + } + } + return TRUE; +} + +gboolean +fu_unifying_bootloader_request (FuUnifyingBootloader *self, + FuUnifyingBootloaderRequest *req, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_length = 0; + guint8 buf_request[32]; + guint8 buf_response[32]; + + /* build packet */ + memset (buf_request, 0x00, sizeof (buf_request)); + buf_request[0x00] = req->cmd; + buf_request[0x01] = req->addr >> 8; + buf_request[0x02] = req->addr & 0xff; + buf_request[0x03] = req->len; + memcpy (buf_request + 0x04, req->data, 28); + + /* send 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, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + HID_REPORT_SET, + 0x0200, 0x0000, + buf_request, + sizeof (buf_request), + &actual_length, + FU_UNIFYING_DEVICE_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error (error, "failed to send data: "); + return FALSE; + } + } + + /* no response required when rebooting */ + if (usb_device != NULL && + req->cmd == FU_UNIFYING_BOOTLOADER_CMD_REBOOT) { + g_autoptr(GError) error_ignore = NULL; + if (!g_usb_device_interrupt_transfer (usb_device, + FU_UNIFYING_DEVICE_EP1, + buf_response, + sizeof (buf_response), + &actual_length, + FU_UNIFYING_DEVICE_TIMEOUT_MS, + NULL, + &error_ignore)) { + g_debug ("ignoring: %s", error_ignore->message); + } else { + if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "device->host", + buf_response, actual_length); + } + } + return TRUE; + } + + /* get response */ + memset (buf_response, 0x00, sizeof (buf_response)); + if (usb_device != NULL) { + if (!g_usb_device_interrupt_transfer (usb_device, + FU_UNIFYING_DEVICE_EP1, + buf_response, + sizeof (buf_response), + &actual_length, + FU_UNIFYING_DEVICE_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error (error, "failed to get data: "); + return FALSE; + } + } else { + /* emulated */ + buf_response[0] = buf_request[0]; + if (buf_response[0] == FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO) { + buf_response[3] = 0x06; /* len */ + buf_response[4] = 0x40; /* lo MSB */ + buf_response[5] = 0x00; /* lo LSB */ + buf_response[6] = 0x6b; /* hi MSB */ + buf_response[7] = 0xff; /* hi LSB */ + buf_response[8] = 0x00; /* bs MSB */ + buf_response[9] = 0x80; /* bs LSB */ + } + actual_length = sizeof (buf_response); + } + 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) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid command response of %02x, expected %02x", + buf_response[0x00], req->cmd); + return FALSE; + } + req->cmd = buf_response[0x00]; + req->addr = ((guint16) buf_response[0x01] << 8) + buf_response[0x02]; + req->len = buf_response[0x03]; + if (req->len > 28) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid data size of %02x", req->len); + return FALSE; + } + memset (req->data, 0x00, 28); + if (req->len > 0) + memcpy (req->data, buf_response + 0x04, req->len); + return TRUE; +} + +static void +fu_unifying_bootloader_init (FuUnifyingBootloader *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_icon (FU_DEVICE (self), "preferences-desktop-keyboard"); + fu_device_set_name (FU_DEVICE (self), "Unifying Receiver"); + fu_device_set_summary (FU_DEVICE (self), "A miniaturised USB wireless receiver (bootloader)"); + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_unifying_bootloader_class_init (FuUnifyingBootloaderClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->to_string = fu_unifying_bootloader_to_string; + klass_device->attach = fu_unifying_bootloader_attach; + klass_device->setup = fu_unifying_bootloader_setup; + klass_usb_device->open = fu_unifying_bootloader_open; + klass_usb_device->close = fu_unifying_bootloader_close; +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader.h fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-usb-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_UNIFYING_BOOTLOADER (fu_unifying_bootloader_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuUnifyingBootloader, fu_unifying_bootloader, FU, UNIFYING_BOOTLOADER, FuUsbDevice) + +struct _FuUnifyingBootloaderClass +{ + FuUsbDeviceClass parent_class; + gboolean (*setup) (FuUnifyingBootloader *self, + GError **error); +}; + +typedef enum { + FU_UNIFYING_BOOTLOADER_CMD_GENERAL_ERROR = 0x01, + FU_UNIFYING_BOOTLOADER_CMD_READ = 0x10, + FU_UNIFYING_BOOTLOADER_CMD_WRITE = 0x20, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_ADDR = 0x21, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_VERIFY_FAIL = 0x22, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_NONZERO_START = 0x23, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_CRC = 0x24, + FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE = 0x30, + FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR = 0x31, + FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START = 0x33, + FU_UNIFYING_BOOTLOADER_CMD_GET_HW_PLATFORM_ID = 0x40, + FU_UNIFYING_BOOTLOADER_CMD_GET_FW_VERSION = 0x50, + FU_UNIFYING_BOOTLOADER_CMD_GET_CHECKSUM = 0x60, + FU_UNIFYING_BOOTLOADER_CMD_REBOOT = 0x70, + FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO = 0x80, + FU_UNIFYING_BOOTLOADER_CMD_GET_BL_VERSION = 0x90, + FU_UNIFYING_BOOTLOADER_CMD_GET_INIT_FW_VERSION = 0xa0, + FU_UNIFYING_BOOTLOADER_CMD_READ_SIGNATURE = 0xb0, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER = 0xc0, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR= 0xc1, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW = 0xc2, + FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM = 0xd0, + FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR = 0xd1, + FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC = 0xd2, + FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID = 0xd3, + FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER = 0xd4, + FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE = 0xe0, + FU_UNIFYING_BOOTLOADER_CMD_LAST +} FuUnifyingBootloaderCmd; + +/* packet to and from device */ +typedef struct __attribute__((packed)) { + guint8 cmd; + guint16 addr; + guint8 len; + guint8 data[28]; +} FuUnifyingBootloaderRequest; + +FuUnifyingBootloaderRequest *fu_unifying_bootloader_request_new (void); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUnifyingBootloaderRequest, g_free); +#pragma clang diagnostic pop + +GPtrArray *fu_unifying_bootloader_parse_requests (FuUnifyingBootloader *self, + GBytes *fw, + GError **error); +gboolean fu_unifying_bootloader_request (FuUnifyingBootloader *self, + FuUnifyingBootloaderRequest *req, + GError **error); + +guint16 fu_unifying_bootloader_get_addr_lo (FuUnifyingBootloader *self); +guint16 fu_unifying_bootloader_get_addr_hi (FuUnifyingBootloader *self); +guint16 fu_unifying_bootloader_get_blocksize (FuUnifyingBootloader *self); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-nordic.c fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-nordic.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-nordic.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-nordic.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-unifying-common.h" +#include "fu-unifying-bootloader-nordic.h" + +struct _FuUnifyingBootloaderNordic +{ + FuUnifyingBootloader parent_instance; +}; + +G_DEFINE_TYPE (FuUnifyingBootloaderNordic, fu_unifying_bootloader_nordic, FU_TYPE_UNIFYING_BOOTLOADER) + +static gchar * +fu_unifying_bootloader_nordic_get_hw_platform_id (FuUnifyingBootloader *self, GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_HW_PLATFORM_ID; + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to get HW ID: "); + return NULL; + } + return g_strndup ((const gchar *) req->data, req->len); +} + +static gchar * +fu_unifying_bootloader_nordic_get_fw_version (FuUnifyingBootloader *self, GError **error) +{ + guint16 micro; + + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_FW_VERSION; + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to get firmware version: "); + return NULL; + } + + /* RRRxx.yy_Bzzzz + * 012345678901234*/ + micro = (guint16) fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; + micro += fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 12); + return fu_unifying_format_version ("RQR", + fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 3), + fu_unifying_buffer_read_uint8 ((const gchar *) req->data + 6), + micro); +} + +static gboolean +fu_unifying_bootloader_nordic_setup (FuUnifyingBootloader *self, GError **error) +{ + g_autofree gchar *hw_platform_id = NULL; + g_autofree gchar *version_fw = NULL; + g_autoptr(GError) error_local = NULL; + + /* get MCU */ + hw_platform_id = fu_unifying_bootloader_nordic_get_hw_platform_id (self, error); + if (hw_platform_id == NULL) + return FALSE; + g_debug ("hw-platform-id=%s", hw_platform_id); + + /* get firmware version, which is not fatal */ + version_fw = fu_unifying_bootloader_nordic_get_fw_version (self, &error_local); + if (version_fw == NULL) { + g_warning ("failed to get firmware version: %s", + error_local->message); + fu_device_set_version (FU_DEVICE (self), "RQR12.00_B0000", + FWUPD_VERSION_FORMAT_PLAIN); + } else { + fu_device_set_version (FU_DEVICE (self), version_fw, + FWUPD_VERSION_FORMAT_PLAIN); + } + + return TRUE; +} + +static gboolean +fu_unifying_bootloader_nordic_write_signature (FuUnifyingBootloader *self, + guint16 addr, guint8 len, const guint8 *data, + GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new(); + req->cmd = 0xC0; + req->addr = addr; + req->len = len; + memcpy (req->data, data, req->len); + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to write sig @0x%02x: ", addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write @%04x: signature is too big", + addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_unifying_bootloader_nordic_write (FuUnifyingBootloader *self, + guint16 addr, guint8 len, const guint8 *data, + GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE; + req->addr = addr; + req->len = len; + if (req->len > 28) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write @%04x: data length too large %02x", + addr, req->len); + return FALSE; + } + memcpy (req->data, data, req->len); + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to transfer fw @0x%02x: ", addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_ADDR) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write @%04x: invalid address", + addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_VERIFY_FAIL) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write @%04x: failed to verify flash content", + addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_NONZERO_START) { + g_debug ("wrote %d bytes at address %04x, value %02x", req->len, + req->addr, req->data[0]); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write @%04x: only 1 byte write of 0xff supported", + addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_INVALID_CRC) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write @%04x: invalid CRC", + addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_unifying_bootloader_nordic_erase (FuUnifyingBootloader *self, guint16 addr, GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE; + req->addr = addr; + req->len = 0x01; + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to erase fw @0x%02x: ", addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to erase @%04x: invalid page", + addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to erase @%04x: byte 0x00 is not 0xff", + addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_unifying_bootloader_nordic_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); + const FuUnifyingBootloaderRequest *payload; + guint16 addr; + g_autoptr(GPtrArray) reqs = NULL; + + /* erase firmware pages up to the bootloader */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + for (addr = fu_unifying_bootloader_get_addr_lo (self); + addr < fu_unifying_bootloader_get_addr_hi (self); + addr += fu_unifying_bootloader_get_blocksize (self)) { + if (!fu_unifying_bootloader_nordic_erase (self, addr, error)) + return FALSE; + } + + /* transfer payload */ + reqs = fu_unifying_bootloader_parse_requests (self, fw, error); + if (reqs == NULL) + return FALSE; + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 1; i < reqs->len; i++) { + gboolean res; + payload = g_ptr_array_index (reqs, i); + + if (payload->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { + res = fu_unifying_bootloader_nordic_write_signature (self, + payload->addr, + payload->len, + payload->data, + error); + } else { + res = fu_unifying_bootloader_nordic_write (self, + payload->addr, + payload->len, + payload->data, + error); + } + + if (!res) + return FALSE; + fu_device_set_progress_full (device, i * 32, reqs->len * 32); + } + + /* send the first managed packet last, excluding the reset vector */ + payload = g_ptr_array_index (reqs, 0); + if (!fu_unifying_bootloader_nordic_write (self, + payload->addr + 1, + payload->len - 1, + payload->data + 1, + error)) + return FALSE; + + if (!fu_unifying_bootloader_nordic_write (self, + 0x0000, + 0x01, + payload->data, + error)) + return FALSE; + + /* mark as complete */ + fu_device_set_progress_full (device, reqs->len * 32, reqs->len * 32); + + /* success! */ + return TRUE; +} + +static void +fu_unifying_bootloader_nordic_class_init (FuUnifyingBootloaderNordicClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUnifyingBootloaderClass *klass_device_bootloader = FU_UNIFYING_BOOTLOADER_CLASS (klass); + klass_device->write_firmware = fu_unifying_bootloader_nordic_write_firmware; + klass_device_bootloader->setup = fu_unifying_bootloader_nordic_setup; +} + +static void +fu_unifying_bootloader_nordic_init (FuUnifyingBootloaderNordic *self) +{ +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-nordic.h fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-nordic.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-nordic.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-nordic.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-unifying-bootloader.h" + +G_BEGIN_DECLS + +#define FU_TYPE_UNIFYING_BOOTLOADER_NORDIC (fu_unifying_bootloader_nordic_get_type ()) +G_DECLARE_FINAL_TYPE (FuUnifyingBootloaderNordic, fu_unifying_bootloader_nordic, FU, UNIFYING_BOOTLOADER_NORDIC, FuUnifyingBootloader) + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-texas.c fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-texas.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-texas.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-texas.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-unifying-common.h" +#include "fu-unifying-bootloader-texas.h" + +struct _FuUnifyingBootloaderTexas +{ + FuUnifyingBootloader parent_instance; +}; + +G_DEFINE_TYPE (FuUnifyingBootloaderTexas, fu_unifying_bootloader_texas, FU_TYPE_UNIFYING_BOOTLOADER) + +static gboolean +fu_unifying_bootloader_texas_erase_all (FuUnifyingBootloader *self, GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; + req->len = 0x01; /* magic number */ + req->data[0] = 0x00; /* magic number */ + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to erase all pages: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_unifying_bootloader_texas_compute_and_test_crc (FuUnifyingBootloader *self, GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; + req->len = 0x01; /* magic number */ + req->data[0] = 0x03; /* magic number */ + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to compute and test CRC: "); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "CRC is incorrect"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_unifying_bootloader_texas_flash_ram_buffer (FuUnifyingBootloader *self, guint16 addr, GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; + req->addr = addr; + req->len = 0x01; /* magic number */ + req->data[0] = 0x01; /* magic number */ + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to flash ram buffer @%04x: ", addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to flash ram buffer @%04x: invalid flash page", + addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to flash ram buffer @%04x: invalid App JMP vector", + addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to flash ram buffer @%04x: page flashed before page 0", + addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_unifying_bootloader_texas_clear_ram_buffer (FuUnifyingBootloader *self, GError **error) +{ + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; + req->addr = 0x0000; + req->len = 0x01; /* magic number */ + req->data[0] = 0x02; /* magic number */ + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, "failed to clear ram buffer @%04x: ", req->addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_unifying_bootloader_texas_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); + const FuUnifyingBootloaderRequest *payload; + g_autoptr(GPtrArray) reqs = NULL; + g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + + /* transfer payload */ + reqs = fu_unifying_bootloader_parse_requests (self, fw, error); + if (reqs == NULL) + return FALSE; + + /* erase all flash pages */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_unifying_bootloader_texas_erase_all (self, error)) + return FALSE; + + /* set existing RAM buffer to 0xff's */ + if (!fu_unifying_bootloader_texas_clear_ram_buffer (self, error)) + return FALSE; + + /* write to RAM buffer */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < reqs->len; i++) { + payload = g_ptr_array_index (reqs, i); + + /* check size */ + if (payload->len != 16) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "payload size invalid @%04x: got 0x%02x", + payload->addr, payload->len); + return FALSE; + } + + /* build packet */ + req->cmd = payload->cmd; + + /* signature addresses do not need to fit inside 128 bytes */ + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) + req->addr = payload->addr; + else + req->addr = payload->addr % 0x80; + + req->len = payload->len; + memcpy (req->data, payload->data, payload->len); + if (!fu_unifying_bootloader_request (self, req, error)) { + g_prefix_error (error, + "failed to write ram buffer @0x%02x: ", + req->addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write ram buffer @%04x: invalid location", + req->addr); + return FALSE; + } + if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write ram buffer @%04x: invalid size 0x%02x", + req->addr, req->len); + return FALSE; + } + + /* flush RAM buffer to EEPROM */ + if ((payload->addr + 0x10) % 0x80 == 0 && + req->cmd != FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) { + guint16 addr_start = payload->addr - (7 * 0x10); + g_debug ("addr flush @ 0x%04x for 0x%04x", + payload->addr, addr_start); + if (!fu_unifying_bootloader_texas_flash_ram_buffer (self, + addr_start, + error)) { + g_prefix_error (error, + "failed to flash ram buffer @0x%04x: ", + addr_start); + return FALSE; + } + } + + /* update progress */ + fu_device_set_progress_full (device, i * 32, reqs->len * 32); + } + + /* check CRC */ + if (!fu_unifying_bootloader_texas_compute_and_test_crc (self, error)) + return FALSE; + + /* mark as complete */ + fu_device_set_progress_full (device, reqs->len * 32, reqs->len * 32); + + /* success! */ + return TRUE; +} + +static gboolean +fu_unifying_bootloader_texas_setup (FuUnifyingBootloader *self, GError **error) +{ + fu_device_set_version (FU_DEVICE (self), "RQR24.00_B0000", + FWUPD_VERSION_FORMAT_PLAIN); + return TRUE; +} + +static void +fu_unifying_bootloader_texas_class_init (FuUnifyingBootloaderTexasClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUnifyingBootloaderClass *klass_device_bootloader = FU_UNIFYING_BOOTLOADER_CLASS (klass); + klass_device->write_firmware = fu_unifying_bootloader_texas_write_firmware; + klass_device_bootloader->setup = fu_unifying_bootloader_texas_setup; +} + +static void +fu_unifying_bootloader_texas_init (FuUnifyingBootloaderTexas *self) +{ +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-texas.h fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-texas.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-bootloader-texas.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-bootloader-texas.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-unifying-bootloader.h" + +G_BEGIN_DECLS + +#define FU_TYPE_UNIFYING_BOOTLOADER_TEXAS (fu_unifying_bootloader_texas_get_type ()) +G_DECLARE_FINAL_TYPE (FuUnifyingBootloaderTexas, fu_unifying_bootloader_texas, FU, UNIFYING_BOOTLOADER_TEXAS, FuUnifyingBootloader) + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-common.c fwupd-1.2.10/plugins/unifying/fu-unifying-common.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "fu-unifying-common.h" + +guint8 +fu_unifying_buffer_read_uint8 (const gchar *str) +{ + guint64 tmp; + gchar buf[3] = { 0x0, 0x0, 0x0 }; + memcpy (buf, str, 2); + tmp = g_ascii_strtoull (buf, NULL, 16); + return tmp; +} + +guint16 +fu_unifying_buffer_read_uint16 (const gchar *str) +{ + guint64 tmp; + gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 }; + memcpy (buf, str, 4); + tmp = g_ascii_strtoull (buf, NULL, 16); + return tmp; +} + +gchar * +fu_unifying_format_version (const gchar *name, guint8 major, guint8 minor, guint16 build) +{ + GString *str = g_string_new (NULL); + for (guint i = 0; i < 3; i++) { + if (g_ascii_isspace (name[i])) + continue; + g_string_append_c (str, name[i]); + } + g_string_append_printf (str, "%02x.%02x_B%04x", major, minor, build); + return g_string_free (str, FALSE); +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-common.h fwupd-1.2.10/plugins/unifying/fu-unifying-common.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define FU_UNIFYING_DEVICE_VID 0x046d + +#define FU_UNIFYING_DEVICE_PID_RUNTIME 0xc52b +#define FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC 0xaaaa +#define FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC_PICO 0xaaae +#define FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS 0xaaac +#define FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS_PICO 0xaaad + +/* Signed firmware are very long to verify on the device */ +#define FU_UNIFYING_DEVICE_TIMEOUT_MS 20000 + +guint8 fu_unifying_buffer_read_uint8 (const gchar *str); +guint16 fu_unifying_buffer_read_uint16 (const gchar *str); + +gchar *fu_unifying_format_version (const gchar *name, + guint8 major, + guint8 minor, + guint16 build); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp.c fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" +#include "fu-unifying-common.h" +#include "fu-unifying-hidpp.h" + +static gchar * +fu_unifying_hidpp_msg_to_string (FuUnifyingHidppMsg *msg) +{ + GString *str = g_string_new (NULL); + const gchar *tmp; + g_autoptr(GError) error = NULL; + g_autoptr(GString) flags_str = g_string_new (NULL); + + g_return_val_if_fail (msg != NULL, NULL); + + if (msg->flags == FU_UNIFYING_HIDPP_MSG_FLAG_NONE) { + g_string_append (flags_str, "none"); + } else { + if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) + g_string_append (flags_str, "longer-timeout,"); + if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID) + g_string_append (flags_str, "ignore-sub-id,"); + if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) + g_string_append (flags_str, "ignore-fnct-id,"); + if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID) + g_string_append (flags_str, "ignore-swid,"); + if (str->len > 0) + g_string_truncate (str, str->len - 1); + } + g_string_append_printf (str, "flags: %02x [%s]\n", + msg->flags, + flags_str->str); + g_string_append_printf (str, "report-id: %02x [%s]\n", + msg->report_id, + fu_unifying_hidpp_msg_rpt_id_to_string (msg)); + tmp = fu_unifying_hidpp_msg_dev_id_to_string (msg); + g_string_append_printf (str, "device-id: %02x [%s]\n", + msg->device_id, tmp ); + g_string_append_printf (str, "sub-id: %02x [%s]\n", + msg->sub_id, + fu_unifying_hidpp_msg_sub_id_to_string (msg)); + g_string_append_printf (str, "function-id: %02x [%s]\n", + msg->function_id, + fu_unifying_hidpp_msg_fcn_id_to_string (msg)); + if (!fu_unifying_hidpp_msg_is_error (msg, &error)) { + g_string_append_printf (str, "error: %s\n", + error->message); + } + return g_string_free (str, FALSE); +} + +gboolean +fu_unifying_hidpp_send (FuIOChannel *io_channel, + FuUnifyingHidppMsg *msg, + guint timeout, + GError **error) +{ + gsize len = fu_unifying_hidpp_msg_get_payload_length (msg); + + /* only for HID++2.0 */ + if (msg->hidpp_version >= 2.f) + msg->function_id |= FU_UNIFYING_HIDPP_MSG_SW_ID; + + /* 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_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; + } + + /* success */ + return TRUE; +} + +gboolean +fu_unifying_hidpp_receive (FuIOChannel *io_channel, + FuUnifyingHidppMsg *msg, + guint timeout, + GError **error) +{ + gsize read_size = 0; + + 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 */ + 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, + G_IO_ERROR_FAILED, + "message length too small, " + "got %" G_GSIZE_FORMAT " expected %" G_GSIZE_FORMAT, + read_size, fu_unifying_hidpp_msg_get_payload_length (msg)); + return FALSE; + } + + /* detailed debugging */ + if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { + g_autofree gchar *str = fu_unifying_hidpp_msg_to_string (msg); + g_print ("%s", str); + } + + /* success */ + return TRUE; +} + +gboolean +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 (); + + /* increase timeout for some operations */ + if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) + timeout *= 10; + + /* send request */ + 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 (io_channel, msg_tmp, timeout, error)) { + g_prefix_error (error, "failed to receive: "); + return FALSE; + } + + /* we don't know how to handle this report packet */ + if (fu_unifying_hidpp_msg_get_payload_length (msg_tmp) == 0x0) { + g_debug ("HID++1.0 report 0x%02x has unknown length, ignoring", + msg_tmp->report_id); + continue; + } + + /* maybe something is also writing to the device? -- + * we can't use the SwID as this is a HID++2.0 feature */ + if (!fu_unifying_hidpp_msg_is_error (msg_tmp, error)) + return FALSE; + + /* is valid reply */ + if (fu_unifying_hidpp_msg_is_reply (msg, msg_tmp)) + break; + + /* to ensure compatibility when an HID++ 2.0 device is + * connected to an HID++ 1.0 receiver, any feature index + * corresponding to an HID++ 1.0 sub-identifier which could be + * sent by the receiver, must be assigned to a dummy feature */ + if (msg->hidpp_version >= 2.f) { + if (fu_unifying_hidpp_msg_is_hidpp10_compat (msg_tmp)) { + g_debug ("ignoring HID++1.0 reply"); + continue; + } + + /* not us */ + if ((msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID) == 0) { + if (!fu_unifying_hidpp_msg_verify_swid (msg_tmp)) { + g_debug ("ignoring reply with SwId 0x%02i, expected 0x%02i", + msg_tmp->function_id & 0x0f, + FU_UNIFYING_HIDPP_MSG_SW_ID); + continue; + } + } + } + + /* hardware not responding */ + if (ignore_cnt++ > 10) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "too many messages to ignore"); + return FALSE; + } + + g_debug ("ignoring message %u", ignore_cnt); + }; + + /* copy over data */ + fu_unifying_hidpp_msg_copy (msg, msg_tmp); + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp.h fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-io-channel.h" + +G_BEGIN_DECLS + +/* + * Based on the HID++ documentation provided by Nestor Lopez Casado at: + * https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing + */ +#define HIDPP_DEVICE_ID_WIRED 0x00 +#define HIDPP_DEVICE_ID_RECEIVER 0xFF +#define HIDPP_DEVICE_ID_UNSET 0xFE + +#define HIDPP_REPORT_NOTIFICATION 0x01 +#define HIDPP_REPORT_ID_SHORT 0x10 +#define HIDPP_REPORT_ID_LONG 0x11 +#define HIDPP_REPORT_ID_VERY_LONG 0x12 + +#define HIDPP_SUBID_VENDOR_SPECIFIC_KEYS 0x03 +#define HIDPP_SUBID_POWER_KEYS 0x04 +#define HIDPP_SUBID_ROLLER 0x05 +#define HIDPP_SUBID_MOUSE_EXTRA_BUTTONS 0x06 +#define HIDPP_SUBID_BATTERY_CHARGING_LEVEL 0x07 +#define HIDPP_SUBID_USER_INTERFACE_EVENT 0x08 +#define HIDPP_SUBID_F_LOCK_STATUS 0x09 +#define HIDPP_SUBID_CALCULATOR_RESULT 0x0A +#define HIDPP_SUBID_MENU_NAVIGATE 0x0B +#define HIDPP_SUBID_FN_KEY 0x0C +#define HIDPP_SUBID_BATTERY_MILEAGE 0x0D +#define HIDPP_SUBID_UART_RX 0x0E +#define HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE 0x17 +#define HIDPP_SUBID_DEVICE_DISCONNECTION 0x40 +#define HIDPP_SUBID_DEVICE_CONNECTION 0x41 +#define HIDPP_SUBID_DEVICE_DISCOVERY 0x42 +#define HIDPP_SUBID_PIN_CODE_REQUEST 0x43 +#define HIDPP_SUBID_RECEIVER_WORKING_MODE 0x44 +#define HIDPP_SUBID_ERROR_MESSAGE 0x45 +#define HIDPP_SUBID_RF_LINK_CHANGE 0x46 +#define HIDPP_SUBID_HCI 0x48 +#define HIDPP_SUBID_LINK_QUALITY 0x49 +#define HIDPP_SUBID_DEVICE_LOCKING_CHANGED 0x4a +#define HIDPP_SUBID_WIRELESS_DEVICE_CHANGE 0x4B +#define HIDPP_SUBID_ACL 0x51 +#define HIDPP_SUBID_VOIP_TELEPHONY_EVENT 0x5B +#define HIDPP_SUBID_LED 0x60 +#define HIDPP_SUBID_GESTURE_AND_AIR 0x65 +#define HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH 0x66 +#define HIDPP_SUBID_TRACEABILITY 0x78 +#define HIDPP_SUBID_SET_REGISTER 0x80 +#define HIDPP_SUBID_GET_REGISTER 0x81 +#define HIDPP_SUBID_SET_LONG_REGISTER 0x82 +#define HIDPP_SUBID_GET_LONG_REGISTER 0x83 +#define HIDPP_SUBID_SET_VERY_LONG_REGISTER 0x84 +#define HIDPP_SUBID_GET_VERY_LONG_REGISTER 0x85 +#define HIDPP_SUBID_ERROR_MSG 0x8F +#define HIDPP_SUBID_ERROR_MSG_20 0xFF + +#define HIDPP_ERR_SUCCESS 0x00 +#define HIDPP_ERR_INVALID_SUBID 0x01 +#define HIDPP_ERR_INVALID_ADDRESS 0x02 +#define HIDPP_ERR_INVALID_VALUE 0x03 +#define HIDPP_ERR_CONNECT_FAIL 0x04 +#define HIDPP_ERR_TOO_MANY_DEVICES 0x05 +#define HIDPP_ERR_ALREADY_EXISTS 0x06 +#define HIDPP_ERR_BUSY 0x07 +#define HIDPP_ERR_UNKNOWN_DEVICE 0x08 +#define HIDPP_ERR_RESOURCE_ERROR 0x09 +#define HIDPP_ERR_REQUEST_UNAVAILABLE 0x0A +#define HIDPP_ERR_INVALID_PARAM_VALUE 0x0B +#define HIDPP_ERR_WRONG_PIN_CODE 0x0C + +/* + * HID++1.0 registers + */ + +#define HIDPP_REGISTER_HIDPP_NOTIFICATIONS 0x00 +#define HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES 0x01 +#define HIDPP_REGISTER_BATTERY_STATUS 0x07 +#define HIDPP_REGISTER_BATTERY_MILEAGE 0x0D +#define HIDPP_REGISTER_PROFILE 0x0F +#define HIDPP_REGISTER_LED_STATUS 0x51 +#define HIDPP_REGISTER_LED_INTENSITY 0x54 +#define HIDPP_REGISTER_LED_COLOR 0x57 +#define HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS 0x61 +#define HIDPP_REGISTER_CURRENT_RESOLUTION 0x63 +#define HIDPP_REGISTER_USB_REFRESH_RATE 0x64 +#define HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT 0xA0 +#define HIDPP_REGISTER_HOT_CONTROL 0xA1 +#define HIDPP_REGISTER_READ_MEMORY 0xA2 +#define HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION 0xB2 +#define HIDPP_REGISTER_PAIRING_INFORMATION 0xB5 +#define HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE 0xF0 +#define HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION 0xF1 + +/* + * HID++2.0 error codes + */ +#define HIDPP_ERROR_CODE_NO_ERROR 0x00 +#define HIDPP_ERROR_CODE_UNKNOWN 0x01 +#define HIDPP_ERROR_CODE_INVALID_ARGUMENT 0x02 +#define HIDPP_ERROR_CODE_OUT_OF_RANGE 0x03 +#define HIDPP_ERROR_CODE_HW_ERROR 0x04 +#define HIDPP_ERROR_CODE_LOGITECH_INTERNAL 0x05 +#define HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX 0x06 +#define HIDPP_ERROR_CODE_INVALID_FUNCTION_ID 0x07 +#define HIDPP_ERROR_CODE_BUSY 0x08 +#define HIDPP_ERROR_CODE_UNSUPPORTED 0x09 + +/* + * HID++2.0 features + */ +#define HIDPP_FEATURE_ROOT 0x0000 +#define HIDPP_FEATURE_I_FEATURE_SET 0x0001 +#define HIDPP_FEATURE_I_FIRMWARE_INFO 0x0003 +#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE 0x0005 +#define HIDPP_FEATURE_DFU_CONTROL 0x00c1 +#define HIDPP_FEATURE_DFU_CONTROL_SIGNED 0x00c2 +#define HIDPP_FEATURE_DFU 0x00d0 +#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS 0x1000 +#define HIDPP_FEATURE_KBD_REPROGRAMMABLE_KEYS 0x1b00 +#define HIDPP_FEATURE_SPECIAL_KEYS_BUTTONS 0x1b04 +#define HIDPP_FEATURE_MOUSE_POINTER_BASIC 0x2200 +#define HIDPP_FEATURE_ADJUSTABLE_DPI 0x2201 +#define HIDPP_FEATURE_ADJUSTABLE_REPORT_RATE 0x8060 +#define HIDPP_FEATURE_COLOR_LED_EFFECTS 0x8070 +#define HIDPP_FEATURE_ONBOARD_PROFILES 0x8100 +#define HIDPP_FEATURE_MOUSE_BUTTON_SPY 0x8110 + +#include "fu-unifying-hidpp-msg.h" + +gboolean fu_unifying_hidpp_send (FuIOChannel *self, + FuUnifyingHidppMsg *msg, + guint timeout, + GError **error); +gboolean fu_unifying_hidpp_receive (FuIOChannel *self, + FuUnifyingHidppMsg *msg, + guint timeout, + GError **error); +gboolean fu_unifying_hidpp_transfer (FuIOChannel *self, + FuUnifyingHidppMsg *msg, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp-msg.c fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp-msg.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp-msg.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp-msg.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-unifying-hidpp.h" +#include "fu-unifying-hidpp-msg.h" + +FuUnifyingHidppMsg * +fu_unifying_hidpp_msg_new (void) +{ + return g_new0 (FuUnifyingHidppMsg, 1); +} + +const gchar * +fu_unifying_hidpp_msg_dev_id_to_string (FuUnifyingHidppMsg *msg) +{ + g_return_val_if_fail (msg != NULL, NULL); + if (msg->device_id == HIDPP_DEVICE_ID_WIRED) + return "wired"; + if (msg->device_id == HIDPP_DEVICE_ID_RECEIVER) + return "receiver"; + if (msg->device_id == HIDPP_DEVICE_ID_UNSET) + return "unset"; + return NULL; +} + +const gchar * +fu_unifying_hidpp_msg_rpt_id_to_string (FuUnifyingHidppMsg *msg) +{ + g_return_val_if_fail (msg != NULL, NULL); + if (msg->report_id == HIDPP_REPORT_ID_SHORT) + return "short"; + if (msg->report_id == HIDPP_REPORT_ID_LONG) + return "long"; + if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) + return "very-long"; + return NULL; +} + +gsize +fu_unifying_hidpp_msg_get_payload_length (FuUnifyingHidppMsg *msg) +{ + if (msg->report_id == HIDPP_REPORT_ID_SHORT) + return 0x07; + if (msg->report_id == HIDPP_REPORT_ID_LONG) + return 0x14; + if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) + return 0x2f; + if (msg->report_id == HIDPP_REPORT_NOTIFICATION) + return 0x08; + return 0x0; +} + +const gchar * +fu_unifying_hidpp_msg_fcn_id_to_string (FuUnifyingHidppMsg *msg) +{ + g_return_val_if_fail (msg != NULL, NULL); + switch (msg->sub_id) { + case HIDPP_SUBID_SET_REGISTER: + case HIDPP_SUBID_GET_REGISTER: + case HIDPP_SUBID_SET_LONG_REGISTER: + case HIDPP_SUBID_GET_LONG_REGISTER: + case HIDPP_SUBID_SET_VERY_LONG_REGISTER: + case HIDPP_SUBID_GET_VERY_LONG_REGISTER: + if (msg->function_id == HIDPP_REGISTER_HIDPP_NOTIFICATIONS) + return "hidpp-notifications"; + if (msg->function_id == HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES) + return "individual-features"; + if (msg->function_id == HIDPP_REGISTER_BATTERY_STATUS) + return "battery-status"; + if (msg->function_id == HIDPP_REGISTER_BATTERY_MILEAGE) + return "battery-mileage"; + if (msg->function_id == HIDPP_REGISTER_PROFILE) + return "profile"; + if (msg->function_id == HIDPP_REGISTER_LED_STATUS) + return "led-status"; + if (msg->function_id == HIDPP_REGISTER_LED_INTENSITY) + return "led-intensity"; + if (msg->function_id == HIDPP_REGISTER_LED_COLOR) + return "led-color"; + if (msg->function_id == HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS) + return "optical-sensor-settings"; + if (msg->function_id == HIDPP_REGISTER_CURRENT_RESOLUTION) + return "current-resolution"; + if (msg->function_id == HIDPP_REGISTER_USB_REFRESH_RATE) + return "usb-refresh-rate"; + if (msg->function_id == HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT) + return "generic-memory-management"; + if (msg->function_id == HIDPP_REGISTER_HOT_CONTROL) + return "hot-control"; + if (msg->function_id == HIDPP_REGISTER_READ_MEMORY) + return "read-memory"; + if (msg->function_id == HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION) + return "device-connection-disconnection"; + if (msg->function_id == HIDPP_REGISTER_PAIRING_INFORMATION) + return "pairing-information"; + if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE) + return "device-firmware-update-mode"; + if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION) + return "device-firmware-information"; + break; + default: + break; + } + return NULL; + +} + +const gchar * +fu_unifying_hidpp_msg_sub_id_to_string (FuUnifyingHidppMsg *msg) +{ + g_return_val_if_fail (msg != NULL, NULL); + if (msg->sub_id == HIDPP_SUBID_VENDOR_SPECIFIC_KEYS) + return "vendor-specific-keys"; + if (msg->sub_id == HIDPP_SUBID_POWER_KEYS) + return "power-keys"; + if (msg->sub_id == HIDPP_SUBID_ROLLER) + return "roller"; + if (msg->sub_id == HIDPP_SUBID_MOUSE_EXTRA_BUTTONS) + return "mouse-extra-buttons"; + if (msg->sub_id == HIDPP_SUBID_BATTERY_CHARGING_LEVEL) + return "battery-charging-level"; + if (msg->sub_id == HIDPP_SUBID_USER_INTERFACE_EVENT) + return "user-interface-event"; + if (msg->sub_id == HIDPP_SUBID_F_LOCK_STATUS) + return "f-lock-status"; + if (msg->sub_id == HIDPP_SUBID_CALCULATOR_RESULT) + return "calculator-result"; + if (msg->sub_id == HIDPP_SUBID_MENU_NAVIGATE) + return "menu-navigate"; + if (msg->sub_id == HIDPP_SUBID_FN_KEY) + return "fn-key"; + if (msg->sub_id == HIDPP_SUBID_BATTERY_MILEAGE) + return "battery-mileage"; + if (msg->sub_id == HIDPP_SUBID_UART_RX) + return "uart-rx"; + if (msg->sub_id == HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE) + return "backlight-duration-update"; + if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCONNECTION) + return "device-disconnection"; + if (msg->sub_id == HIDPP_SUBID_DEVICE_CONNECTION) + return "device-connection"; + if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCOVERY) + return "device-discovery"; + if (msg->sub_id == HIDPP_SUBID_PIN_CODE_REQUEST) + return "pin-code-request"; + if (msg->sub_id == HIDPP_SUBID_RECEIVER_WORKING_MODE) + return "receiver-working-mode"; + if (msg->sub_id == HIDPP_SUBID_ERROR_MESSAGE) + return "error-message"; + if (msg->sub_id == HIDPP_SUBID_RF_LINK_CHANGE) + return "rf-link-change"; + if (msg->sub_id == HIDPP_SUBID_HCI) + return "hci"; + if (msg->sub_id == HIDPP_SUBID_LINK_QUALITY) + return "link-quality"; + if (msg->sub_id == HIDPP_SUBID_DEVICE_LOCKING_CHANGED) + return "device-locking-changed"; + if (msg->sub_id == HIDPP_SUBID_WIRELESS_DEVICE_CHANGE) + return "wireless-device-change"; + if (msg->sub_id == HIDPP_SUBID_ACL) + return "acl"; + if (msg->sub_id == HIDPP_SUBID_VOIP_TELEPHONY_EVENT) + return "voip-telephony-event"; + if (msg->sub_id == HIDPP_SUBID_LED) + return "led"; + if (msg->sub_id == HIDPP_SUBID_GESTURE_AND_AIR) + return "gesture-and-air"; + if (msg->sub_id == HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH) + return "touchpad-multi-touch"; + if (msg->sub_id == HIDPP_SUBID_TRACEABILITY) + return "traceability"; + if (msg->sub_id == HIDPP_SUBID_SET_REGISTER) + return "set-register"; + if (msg->sub_id == HIDPP_SUBID_GET_REGISTER) + return "get-register"; + if (msg->sub_id == HIDPP_SUBID_SET_LONG_REGISTER) + return "set-long-register"; + if (msg->sub_id == HIDPP_SUBID_GET_LONG_REGISTER) + return "get-long-register"; + if (msg->sub_id == HIDPP_SUBID_SET_VERY_LONG_REGISTER) + return "set-very-long-register"; + if (msg->sub_id == HIDPP_SUBID_GET_VERY_LONG_REGISTER) + return "get-very-long-register"; + if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) + return "error-msg"; + if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) + return "error-msg-v2"; + return NULL; +} + +gboolean +fu_unifying_hidpp_msg_is_reply (FuUnifyingHidppMsg *msg1, FuUnifyingHidppMsg *msg2) +{ + g_return_val_if_fail (msg1 != NULL, FALSE); + g_return_val_if_fail (msg2 != NULL, FALSE); + if (msg1->device_id != msg2->device_id && + msg1->device_id != HIDPP_DEVICE_ID_UNSET && + msg2->device_id != HIDPP_DEVICE_ID_UNSET) + return FALSE; + if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID || + msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID) + return TRUE; + if (msg1->sub_id != msg2->sub_id) + return FALSE; + if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID || + msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) + return TRUE; + if (msg1->function_id != msg2->function_id) + return FALSE; + return TRUE; +} + +/* HID++ error */ +gboolean +fu_unifying_hidpp_msg_is_error (FuUnifyingHidppMsg *msg, GError **error) +{ + g_return_val_if_fail (msg != NULL, FALSE); + if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) { + switch (msg->data[1]) { + case HIDPP_ERR_INVALID_SUBID: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "invalid SubID"); + break; + case HIDPP_ERR_INVALID_ADDRESS: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid address"); + break; + case HIDPP_ERR_INVALID_VALUE: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid value"); + break; + case HIDPP_ERR_CONNECT_FAIL: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "connection request failed"); + break; + case HIDPP_ERR_TOO_MANY_DEVICES: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NO_SPACE, + "too many devices connected"); + break; + case HIDPP_ERR_ALREADY_EXISTS: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_EXISTS, + "already exists"); + break; + case HIDPP_ERR_BUSY: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_BUSY, + "busy"); + break; + case HIDPP_ERR_UNKNOWN_DEVICE: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "unknown device"); + break; + case HIDPP_ERR_RESOURCE_ERROR: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_HOST_UNREACHABLE, + "resource error"); + break; + case HIDPP_ERR_REQUEST_UNAVAILABLE: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_EXISTS, + "request not valid in current context"); + break; + case HIDPP_ERR_INVALID_PARAM_VALUE: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "request parameter has unsupported value"); + break; + case HIDPP_ERR_WRONG_PIN_CODE: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CONNECTION_REFUSED, + "the pin code was wrong"); + break; + default: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "generic failure"); + } + return FALSE; + } + if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) { + switch (msg->data[1]) { + case HIDPP_ERROR_CODE_INVALID_ARGUMENT: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Invalid argument 0x%02x", + msg->data[2]); + break; + case HIDPP_ERROR_CODE_OUT_OF_RANGE: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "out of range"); + break; + case HIDPP_ERROR_CODE_HW_ERROR: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_BROKEN_PIPE, + "hardware error"); + break; + case HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "invalid feature index"); + break; + case HIDPP_ERROR_CODE_INVALID_FUNCTION_ID: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "invalid function ID"); + break; + case HIDPP_ERROR_CODE_BUSY: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_BUSY, + "busy"); + break; + case HIDPP_ERROR_CODE_UNSUPPORTED: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "unsupported"); + break; + default: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "generic failure"); + break; + } + return FALSE; + } + return TRUE; +} + +void +fu_unifying_hidpp_msg_copy (FuUnifyingHidppMsg *msg_dst, const FuUnifyingHidppMsg *msg_src) +{ + g_return_if_fail (msg_dst != NULL); + g_return_if_fail (msg_src != NULL); + memset (msg_dst->data, 0x00, sizeof(msg_dst->data)); + msg_dst->device_id = msg_src->device_id; + msg_dst->sub_id = msg_src->sub_id; + msg_dst->function_id = msg_src->function_id; + memcpy (msg_dst->data, msg_src->data, sizeof(msg_dst->data)); +} + +/* filter HID++1.0 messages */ +gboolean +fu_unifying_hidpp_msg_is_hidpp10_compat (FuUnifyingHidppMsg *msg) +{ + g_return_val_if_fail (msg != NULL, FALSE); + if (msg->sub_id == 0x40 || + msg->sub_id == 0x41 || + msg->sub_id == 0x49 || + msg->sub_id == 0x4b || + msg->sub_id == 0x8f) { + return TRUE; + } + return FALSE; +} + +gboolean +fu_unifying_hidpp_msg_verify_swid (FuUnifyingHidppMsg *msg) +{ + g_return_val_if_fail (msg != NULL, FALSE); + if ((msg->function_id & 0x0f) != FU_UNIFYING_HIDPP_MSG_SW_ID) + return FALSE; + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp-msg.h fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp-msg.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-hidpp-msg.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-hidpp-msg.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +typedef enum { + FU_UNIFYING_HIDPP_MSG_FLAG_NONE, + FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT = 1 << 0, + FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID = 1 << 1, + FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID = 1 << 2, + FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID = 1 << 3, + /*< private >*/ + FU_UNIFYING_HIDPP_MSG_FLAG_LAST +} FuUnifyingHidppMsgFlags; + +typedef struct __attribute__((packed)) { + guint8 report_id; + guint8 device_id; + guint8 sub_id; + guint8 function_id; /* funcId:software_id */ + guint8 data[47]; /* maximum supported by Windows XP SP2 */ + /* not included in the packet sent to the hardware */ + guint32 flags; + guint8 hidpp_version; +} FuUnifyingHidppMsg; + +/* this is specific to fwupd */ +#define FU_UNIFYING_HIDPP_MSG_SW_ID 0x07 + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUnifyingHidppMsg, g_free); +#pragma clang diagnostic pop + +FuUnifyingHidppMsg *fu_unifying_hidpp_msg_new (void); +void fu_unifying_hidpp_msg_copy (FuUnifyingHidppMsg *msg_dst, + const FuUnifyingHidppMsg *msg_src); +gsize fu_unifying_hidpp_msg_get_payload_length (FuUnifyingHidppMsg *msg); +gboolean fu_unifying_hidpp_msg_is_reply (FuUnifyingHidppMsg *msg1, + FuUnifyingHidppMsg *msg2); +gboolean fu_unifying_hidpp_msg_is_hidpp10_compat (FuUnifyingHidppMsg *msg); +gboolean fu_unifying_hidpp_msg_is_error (FuUnifyingHidppMsg *msg, + GError **error); +gboolean fu_unifying_hidpp_msg_verify_swid (FuUnifyingHidppMsg *msg); + +const gchar *fu_unifying_hidpp_msg_dev_id_to_string (FuUnifyingHidppMsg *msg); +const gchar *fu_unifying_hidpp_msg_rpt_id_to_string (FuUnifyingHidppMsg *msg); +const gchar *fu_unifying_hidpp_msg_sub_id_to_string (FuUnifyingHidppMsg *msg); +const gchar *fu_unifying_hidpp_msg_fcn_id_to_string (FuUnifyingHidppMsg *msg); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-peripheral.c fwupd-1.2.10/plugins/unifying/fu-unifying-peripheral.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-peripheral.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-peripheral.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,1007 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-unifying-common.h" +#include "fu-unifying-peripheral.h" +#include "fu-unifying-hidpp.h" + +struct _FuUnifyingPeripheral +{ + FuUdevDevice parent_instance; + guint8 battery_level; + guint8 cached_fw_entity; + guint8 hidpp_id; + guint8 hidpp_version; + gboolean is_updatable; + gboolean is_active; + FuIOChannel *io_channel; + GPtrArray *feature_index; /* of FuUnifyingHidppMap */ +}; + +typedef struct { + guint8 idx; + guint16 feature; +} FuUnifyingHidppMap; + +G_DEFINE_TYPE (FuUnifyingPeripheral, fu_unifying_peripheral, FU_TYPE_UDEV_DEVICE) + +typedef enum { + FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD, + FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL, + FU_UNIFYING_PERIPHERAL_KIND_NUMPAD, + FU_UNIFYING_PERIPHERAL_KIND_MOUSE, + FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD, + FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL, + FU_UNIFYING_PERIPHERAL_KIND_PRESENTER, + FU_UNIFYING_PERIPHERAL_KIND_RECEIVER, + FU_UNIFYING_PERIPHERAL_KIND_LAST +} FuUnifyingPeripheralKind; + +static const gchar * +fu_unifying_peripheral_get_icon (FuUnifyingPeripheralKind kind) +{ + if (kind == FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD) + return "input-keyboard"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL) + return "pda"; // ish + if (kind == FU_UNIFYING_PERIPHERAL_KIND_NUMPAD) + return "input-dialpad"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_MOUSE) + return "input-mouse"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD) + return "input-touchpad"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL) + return "input-mouse"; // ish + if (kind == FU_UNIFYING_PERIPHERAL_KIND_PRESENTER) + return "pda"; // ish + if (kind == FU_UNIFYING_PERIPHERAL_KIND_RECEIVER) + return "preferences-desktop-keyboard"; + return NULL; +} + +static const gchar * +fu_unifying_peripheral_get_summary (FuUnifyingPeripheralKind kind) +{ + if (kind == FU_UNIFYING_PERIPHERAL_KIND_KEYBOARD) + return "Unifying Keyboard"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_REMOTE_CONTROL) + return "Unifying Remote Control"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_NUMPAD) + return "Unifying Number Pad"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_MOUSE) + return "Unifying Mouse"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_TOUCHPAD) + return "Unifying Touchpad"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_TRACKBALL) + return "Unifying Trackball"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_PRESENTER) + return "Unifying Presenter"; + if (kind == FU_UNIFYING_PERIPHERAL_KIND_RECEIVER) + return "Unifying Receiver"; + return NULL; +} + +static const gchar * +fu_unifying_hidpp_feature_to_string (guint16 feature) +{ + if (feature == HIDPP_FEATURE_ROOT) + return "Root"; + if (feature == HIDPP_FEATURE_I_FIRMWARE_INFO) + return "IFirmwareInfo"; + if (feature == HIDPP_FEATURE_GET_DEVICE_NAME_TYPE) + return "GetDevicenameType"; + if (feature == HIDPP_FEATURE_BATTERY_LEVEL_STATUS) + return "BatteryLevelStatus"; + if (feature == HIDPP_FEATURE_DFU_CONTROL) + return "DfuControl"; + if (feature == HIDPP_FEATURE_DFU_CONTROL_SIGNED) + return "DfuControlSigned"; + if (feature == HIDPP_FEATURE_DFU) + return "Dfu"; + return NULL; +} + +static void +fu_unifying_peripheral_refresh_updatable (FuUnifyingPeripheral *self) +{ + /* device can only be upgraded if it is capable, and active */ + if (self->is_updatable && self->is_active) { + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + return; + } + fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static gboolean +fu_unifying_peripheral_ping (FuUnifyingPeripheral *self, GError **error) +{ + gdouble version; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + + /* handle failure */ + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = 0x00; /* rootIndex */ + msg->function_id = 0x01 << 4; /* ping */ + msg->data[0] = 0x00; + msg->data[1] = 0x00; + msg->data[2] = 0xaa; /* user-selected value */ + msg->hidpp_version = self->hidpp_version; + 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)) { + self->hidpp_version = 1; + return TRUE; + } + if (g_error_matches (error_local, + G_IO_ERROR, + G_IO_ERROR_HOST_UNREACHABLE)) { + self->is_active = FALSE; + fu_unifying_peripheral_refresh_updatable (self); + return TRUE; + } + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to ping %s: %s", + fu_device_get_name (FU_DEVICE (self)), + error_local->message); + return FALSE; + } + + /* device no longer asleep */ + self->is_active = TRUE; + fu_unifying_peripheral_refresh_updatable (self); + + /* if the HID++ ID is unset, grab it from the reply */ + if (self->hidpp_id == HIDPP_DEVICE_ID_UNSET) { + self->hidpp_id = msg->device_id; + g_debug ("HID++ ID is %02x", self->hidpp_id); + } + + /* format version in BCD format */ + version = (gdouble) msg->data[0] + ((gdouble) msg->data[1]) / 100.f; + self->hidpp_version = (guint) version; + + /* success */ + return TRUE; +} + +static gboolean +fu_unifying_peripheral_close (FuDevice *device, GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + if (!fu_io_channel_shutdown (self->io_channel, error)) + return FALSE; + g_clear_object (&self->io_channel); + return TRUE; +} + +static gboolean +fu_unifying_peripheral_poll (FuDevice *device, GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + const guint timeout = 1; /* ms */ + g_autoptr(GError) error_local = NULL; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open */ + locker = fu_device_locker_new (self, error); + if (locker == NULL) + return FALSE; + + /* flush pending data */ + msg->device_id = self->hidpp_id; + msg->hidpp_version = self->hidpp_version; + 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)) { + g_warning ("failed to get pending read: %s", error_local->message); + return TRUE; + } + /* no data to receive */ + g_clear_error (&error_local); + } + + /* just ping */ + if (!fu_unifying_peripheral_ping (self, &error_local)) { + g_warning ("failed to ping device: %s", error_local->message); + return TRUE; + } + + /* this is the first time the device has been active */ + if (self->feature_index->len == 0) { + fu_device_probe_invalidate (FU_DEVICE (self)); + if (!fu_device_setup (FU_DEVICE (self), error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_unifying_peripheral_open (FuDevice *device, GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + const gchar *devpath = g_udev_device_get_device_file (udev_device); + + /* open */ + self->io_channel = fu_io_channel_new_file (devpath, error); + if (self->io_channel == NULL) + return FALSE; + + return TRUE; +} + +static void +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->hidpp_id != HIDPP_DEVICE_ID_UNSET) + g_string_append_printf (str, " HidppId:\t\t0x%02x\n", (guint) self->hidpp_id); + if (self->battery_level != 0) + g_string_append_printf (str, " Battery-level:\t\t%u\n", self->battery_level); + g_string_append_printf (str, " IsUpdatable:\t\t%i\n", self->is_updatable); + g_string_append_printf (str, " IsActive:\t\t%i\n", self->is_active); + for (guint i = 0; i < self->feature_index->len; i++) { + FuUnifyingHidppMap *map = g_ptr_array_index (self->feature_index, i); + g_string_append_printf (str, " Feature%02x:\t\t%s [0x%04x]\n", + map->idx, + fu_unifying_hidpp_feature_to_string (map->feature), + map->feature); + } +} + +static guint8 +fu_unifying_peripheral_feature_get_idx (FuUnifyingPeripheral *self, guint16 feature) +{ + for (guint i = 0; i < self->feature_index->len; i++) { + FuUnifyingHidppMap *map = g_ptr_array_index (self->feature_index, i); + if (map->feature == feature) + return map->idx; + } + return 0x00; +} + +static gboolean +fu_unifying_peripheral_fetch_firmware_info (FuUnifyingPeripheral *self, GError **error) +{ + guint8 idx; + guint8 entity_count; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + + /* get the feature index */ + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_I_FIRMWARE_INFO); + if (idx == 0x00) + return TRUE; + + /* get the entity count */ + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x00 << 4; /* getCount */ + msg->hidpp_version = self->hidpp_version; + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { + g_prefix_error (error, "failed to get firmware count: "); + return FALSE; + } + entity_count = msg->data[0]; + g_debug ("firmware entity count is %u", entity_count); + + /* get firmware, bootloader, hardware versions */ + for (guint8 i = 0; i < entity_count; i++) { + guint16 build; + g_autofree gchar *version = NULL; + g_autofree gchar *name = NULL; + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x01 << 4; /* getInfo */ + msg->data[0] = i; + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { + g_prefix_error (error, "failed to get firmware info: "); + return FALSE; + } + if (msg->data[1] == 0x00 && + msg->data[2] == 0x00 && + msg->data[3] == 0x00 && + msg->data[4] == 0x00 && + msg->data[5] == 0x00 && + msg->data[6] == 0x00 && + msg->data[7] == 0x00) { + g_debug ("no version set for entity %u", i); + continue; + } + name = g_strdup_printf ("%c%c%c", + msg->data[1], + msg->data[2], + msg->data[3]); + build = ((guint16) msg->data[6]) << 8 | msg->data[7]; + version = fu_unifying_format_version (name, + msg->data[4], + msg->data[5], + build); + g_debug ("firmware entity 0x%02x version is %s", i, version); + if (msg->data[0] == 0) { + fu_device_set_version (FU_DEVICE (self), version, + FWUPD_VERSION_FORMAT_PLAIN); + self->cached_fw_entity = i; + } else if (msg->data[0] == 1) { + fu_device_set_version_bootloader (FU_DEVICE (self), version); + } else if (msg->data[0] == 2) { + fu_device_set_metadata (FU_DEVICE (self), "version-hw", version); + } + } + + /* not an error, the device just doesn't support this */ + return TRUE; +} + +static gboolean +fu_unifying_peripheral_fetch_battery_level (FuUnifyingPeripheral *self, GError **error) +{ + /* try using HID++2.0 */ + if (self->hidpp_version >= 2.f) { + guint8 idx; + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_BATTERY_LEVEL_STATUS); + if (idx != 0x00) { + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x00; /* GetBatteryLevelStatus */ + msg->hidpp_version = self->hidpp_version; + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { + g_prefix_error (error, "failed to get battery info: "); + return FALSE; + } + if (msg->data[0] != 0x00) + self->battery_level = msg->data[0]; + return TRUE; + } + } + + /* try HID++1.0 battery mileage */ + if (self->hidpp_version == 1.f) { + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + 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->io_channel, msg, NULL)) { + if (msg->data[0] != 0x00) + self->battery_level = msg->data[0]; + return TRUE; + } + + /* try HID++1.0 battery status instead */ + msg->function_id = HIDPP_REGISTER_BATTERY_STATUS; + if (fu_unifying_hidpp_transfer (self->io_channel, msg, NULL)) { + switch (msg->data[0]) { + case 1: /* 0 - 10 */ + self->battery_level = 5; + break; + case 3: /* 11 - 30 */ + self->battery_level = 20; + break; + case 5: /* 31 - 80 */ + self->battery_level = 55; + break; + case 7: /* 81 - 100 */ + self->battery_level = 90; + break; + default: + g_warning ("unknown battery percentage: 0x%02x", + msg->data[0]); + break; + } + return TRUE; + } + } + + /* not an error, the device just doesn't support any of the methods */ + return TRUE; +} + +static gboolean +fu_unifying_hidpp_feature_search (FuDevice *device, guint16 feature, GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + FuUnifyingHidppMap *map; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + + /* find the idx for the feature */ + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = 0x00; /* rootIndex */ + msg->function_id = 0x00 << 4; /* getFeature */ + msg->data[0] = feature >> 8; + msg->data[1] = feature; + msg->data[2] = 0x00; + msg->hidpp_version = self->hidpp_version; + 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); + return FALSE; + } + + /* zero index */ + if (msg->data[0] == 0x00) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "feature %s [0x%04x] not found", + fu_unifying_hidpp_feature_to_string (feature), feature); + return FALSE; + } + + /* add to map */ + map = g_new0 (FuUnifyingHidppMap, 1); + map->idx = msg->data[0]; + map->feature = feature; + g_ptr_array_add (self->feature_index, map); + g_debug ("added feature %s [0x%04x] as idx %02x", + fu_unifying_hidpp_feature_to_string (feature), feature, map->idx); + return TRUE; +} + +static gboolean +fu_unifying_peripheral_probe (FuUdevDevice *device, GError **error) +{ + g_autofree gchar *devid = NULL; + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id (device, "hid", error)) + return FALSE; + + /* nearly... */ + fu_device_set_vendor_id (FU_DEVICE (device), "USB:0x046D"); + + /* this is a non-standard extension */ + devid = g_strdup_printf ("UFY\\VID_%04X&PID_%04X", + fu_udev_device_get_vendor (device), + fu_udev_device_get_model (device)); + fu_device_add_instance_id (FU_DEVICE (device), devid); + return TRUE; +} + +static gboolean +fu_unifying_peripheral_setup (FuDevice *device, GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + guint8 idx; + const guint16 map_features[] = { + HIDPP_FEATURE_GET_DEVICE_NAME_TYPE, + HIDPP_FEATURE_I_FIRMWARE_INFO, + HIDPP_FEATURE_BATTERY_LEVEL_STATUS, + HIDPP_FEATURE_DFU_CONTROL, + HIDPP_FEATURE_DFU_CONTROL_SIGNED, + HIDPP_FEATURE_DFU, + HIDPP_FEATURE_ROOT }; + + /* ping device to get HID++ version */ + if (!fu_unifying_peripheral_ping (self, error)) + return FALSE; + + /* add known root for HID++2.0 */ + g_ptr_array_set_size (self->feature_index, 0); + if (self->hidpp_version >= 2.f) { + FuUnifyingHidppMap *map = g_new0 (FuUnifyingHidppMap, 1); + map->idx = 0x00; + map->feature = HIDPP_FEATURE_ROOT; + g_ptr_array_add (self->feature_index, map); + } + + /* map some *optional* HID++2.0 features we might use */ + for (guint i = 0; map_features[i] != HIDPP_FEATURE_ROOT; i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_unifying_hidpp_feature_search (device, + map_features[i], + &error_local)) { + g_debug ("%s", error_local->message); + if (g_error_matches (error_local, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT) || + g_error_matches (error_local, + G_IO_ERROR, + G_IO_ERROR_HOST_UNREACHABLE)) { + /* timed out, so not trying any more */ + break; + } + } + } + + /* get the firmware information */ + if (!fu_unifying_peripheral_fetch_firmware_info (self, error)) + return FALSE; + + /* get the battery level */ + if (!fu_unifying_peripheral_fetch_battery_level (self, error)) + return FALSE; + + /* try using HID++2.0 */ + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE); + if (idx != 0x00) { + const gchar *tmp; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x02 << 4; /* getDeviceType */ + msg->hidpp_version = self->hidpp_version; + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { + g_prefix_error (error, "failed to get device type: "); + return FALSE; + } + + /* add nice-to-have data */ + tmp = fu_unifying_peripheral_get_summary (msg->data[0]); + if (tmp != NULL) + fu_device_set_summary (FU_DEVICE (device), tmp); + tmp = fu_unifying_peripheral_get_icon (msg->data[0]); + if (tmp != NULL) + fu_device_add_icon (FU_DEVICE (device), tmp); + } + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL); + if (idx != 0x00) { + self->is_updatable = TRUE; + fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL_SIGNED); + if (idx != 0x00) { + /* check the feature is available */ + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x00 << 4; /* getDfuStatus */ + msg->hidpp_version = self->hidpp_version; + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { + g_prefix_error (error, "failed to get DFU status: "); + return FALSE; + } + if ((msg->data[2] & 0x01) > 0) { + g_warning ("DFU mode not available"); + } else { + self->is_updatable = TRUE; + fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + } + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); + if (idx != 0x00) { + self->is_updatable = TRUE; + fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + if (fu_device_get_version (device) == NULL) { + g_debug ("repairing device in bootloader mode"); + fu_device_set_version (FU_DEVICE (device), + "MPK00.00_B0000", + FWUPD_VERSION_FORMAT_PLAIN); + } + } + + /* this device may have changed state */ + fu_unifying_peripheral_refresh_updatable (self); + + /* poll for pings to track active state */ + fu_device_set_poll_interval (device, 30000); + return TRUE; +} + +static gboolean +fu_unifying_peripheral_detach (FuDevice *device, GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + guint8 idx; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + + /* this requires user action */ + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL); + if (idx != 0x00) { + msg->report_id = HIDPP_REPORT_ID_LONG; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x01 << 4; /* setDfuControl */ + msg->data[0] = 0x01; /* enterDfu */ + msg->data[1] = 0x00; /* dfuControlParam */ + msg->data[2] = 0x00; /* unused */ + msg->data[3] = 0x00; /* unused */ + msg->data[4] = 'D'; + msg->data[5] = 'F'; + msg->data[6] = 'U'; + 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->io_channel, msg, error)) { + g_prefix_error (error, "failed to put device into DFU mode: "); + return FALSE; + } + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + + /* this can reboot all by itself */ + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU_CONTROL_SIGNED); + if (idx != 0x00) { + msg->report_id = HIDPP_REPORT_ID_LONG; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x01 << 4; /* setDfuControl */ + msg->data[0] = 0x01; /* startDfu */ + msg->data[1] = 0x00; /* dfuControlParam */ + msg->data[2] = 0x00; /* unused */ + msg->data[3] = 0x00; /* unused */ + msg->data[4] = 'D'; + msg->data[5] = 'F'; + msg->data[6] = 'U'; + msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID; + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { + g_prefix_error (error, "failed to put device into DFU mode: "); + return FALSE; + } + return fu_unifying_peripheral_setup (FU_DEVICE (self), error); + } + + /* we don't know how */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "no method to detach"); + return FALSE; +} + +static gboolean +fu_unifying_peripheral_check_status (guint8 status, GError **error) +{ + switch (status & 0x7f) { + case 0x00: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid status value 0x%02x", + status); + break; + case 0x01: /* packet success */ + case 0x02: /* DFU success */ + case 0x05: /* DFU success: entity restart required */ + case 0x06: /* DFU success: system restart required */ + /* success */ + return TRUE; + break; + case 0x03: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_PENDING, + "wait for event (command in progress)"); + break; + case 0x04: + case 0x10: /* unknown */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "generic error"); + break; + case 0x11: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "bad voltage (power too low?)"); + break; + case 0x12: + case 0x14: /* bad magic string */ + case 0x21: /* bad firmware */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unsupported firmware"); + break; + case 0x13: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unsupported encryption mode"); + break; + case 0x15: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "erase failure"); + break; + case 0x16: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "DFU not started"); + break; + case 0x17: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "bad sequence number"); + break; + case 0x18: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unsupported command"); + break; + case 0x19: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "command in progress"); + break; + case 0x1a: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "address out of range"); + break; + case 0x1b: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unaligned address"); + break; + case 0x1c: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "bad size"); + break; + case 0x1d: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "missing program data"); + break; + case 0x1e: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "missing check data"); + break; + case 0x1f: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "program failed to write"); + break; + case 0x20: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "program failed to verify"); + break; + case 0x22: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "firmware check failure"); + break; + case 0x23: + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "blocked command (restart required)"); + break; + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unhandled status value 0x%02x", + status); + break; + } + return FALSE; +} + +static gboolean +fu_unifying_peripheral_write_firmware_pkt (FuUnifyingPeripheral *self, + guint8 idx, + guint8 cmd, + const guint8 *data, + GError **error) +{ + guint32 packet_cnt; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + g_autoptr(GError) error_local = NULL; + + /* send firmware data */ + msg->report_id = HIDPP_REPORT_ID_LONG; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + 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->io_channel, msg, &error_local)) { + g_prefix_error (error, "failed to supply program data: "); + return FALSE; + } + + /* check error */ + packet_cnt = fu_common_read_uint32 (msg->data, G_BIG_ENDIAN); + g_debug ("packet_cnt=0x%04x", packet_cnt); + if (fu_unifying_peripheral_check_status (msg->data[4], &error_local)) + return TRUE; + + /* fatal error */ + if (!g_error_matches (error_local, + G_IO_ERROR, + G_IO_ERROR_PENDING)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + error_local->message); + return FALSE; + } + + /* wait for the HID++ notification */ + g_debug ("ignoring: %s", error_local->message); + 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->io_channel, msg2, 15000, error)) + return FALSE; + if (fu_unifying_hidpp_msg_is_reply (msg, msg2)) { + g_autoptr(GError) error2 = NULL; + if (!fu_unifying_peripheral_check_status (msg2->data[4], &error2)) { + g_debug ("got %s, waiting a bit longer", error2->message); + continue; + } + return TRUE; + } else { + g_debug ("got wrong packet, continue to wait..."); + } + } + + /* nothing in the queue */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to get event after timeout"); + return FALSE; +} + +static gboolean +fu_unifying_peripheral_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + gsize sz = 0; + const guint8 *data; + guint8 cmd = 0x04; + guint8 idx; + + /* if we're in bootloader mode, we should be able to get this feature */ + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); + if (idx == 0x00) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "no DFU feature available"); + return FALSE; + } + + /* flash hardware */ + data = g_bytes_get_data (fw, &sz); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (gsize i = 0; i < sz / 16; i++) { + + /* send packet and wait for reply */ + g_debug ("send data at addr=0x%04x", (guint) i * 16); + if (!fu_unifying_peripheral_write_firmware_pkt (self, + idx, + cmd, + data + (i * 16), + error)) { + g_prefix_error (error, + "failed to write @0x%04x: ", + (guint) i * 16); + return FALSE; + } + + /* use sliding window */ + cmd = (cmd + 1) % 4; + + /* update progress-bar */ + fu_device_set_progress_full (device, i * 16, sz); + } + + return TRUE; +} + +static gboolean +fu_unifying_peripheral_attach (FuDevice *device, GError **error) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); + guint8 idx; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + + /* if we're in bootloader mode, we should be able to get this feature */ + idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); + if (idx == 0x00) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "no DFU feature available"); + return FALSE; + } + + /* reboot back into firmware mode */ + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = self->hidpp_id; + msg->sub_id = idx; + msg->function_id = 0x05 << 4; /* restart */ + msg->data[0] = self->cached_fw_entity; /* fwEntity */ + msg->hidpp_version = self->hidpp_version; + 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->io_channel, msg, error)) { + g_prefix_error (error, "failed to restart device: "); + return FALSE; + } + + /* reprobe */ + if (!fu_unifying_peripheral_setup (device, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_unifying_peripheral_finalize (GObject *object) +{ + FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (object); + g_ptr_array_unref (self->feature_index); + G_OBJECT_CLASS (fu_unifying_peripheral_parent_class)->finalize (object); +} + +static void +fu_unifying_peripheral_class_init (FuUnifyingPeripheralClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = fu_unifying_peripheral_finalize; + klass_device->setup = fu_unifying_peripheral_setup; + klass_device->open = fu_unifying_peripheral_open; + klass_device->close = fu_unifying_peripheral_close; + klass_device->write_firmware = fu_unifying_peripheral_write_firmware; + klass_device->attach = fu_unifying_peripheral_attach; + klass_device->detach = fu_unifying_peripheral_detach; + klass_device->poll = fu_unifying_peripheral_poll; + klass_device->to_string = fu_unifying_peripheral_to_string; + klass_device_udev->probe = fu_unifying_peripheral_probe; +} + +static void +fu_unifying_peripheral_init (FuUnifyingPeripheral *self) +{ + self->hidpp_id = HIDPP_DEVICE_ID_UNSET; + self->feature_index = g_ptr_array_new_with_free_func (g_free); + fu_device_add_parent_guid (FU_DEVICE (self), "HIDRAW\\VEN_046D&DEV_C52B"); + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-peripheral.h fwupd-1.2.10/plugins/unifying/fu-unifying-peripheral.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-peripheral.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-peripheral.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-udev-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_UNIFYING_PERIPHERAL (fu_unifying_peripheral_get_type ()) +G_DECLARE_FINAL_TYPE (FuUnifyingPeripheral, fu_unifying_peripheral, FU, UNIFYING_PERIPHERAL, FuUdevDevice) + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-runtime.c fwupd-1.2.10/plugins/unifying/fu-unifying-runtime.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-runtime.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-runtime.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-unifying-common.h" +#include "fu-unifying-runtime.h" +#include "fu-unifying-hidpp.h" + +struct _FuUnifyingRuntime +{ + FuUdevDevice parent_instance; + guint8 version_bl_major; + gboolean signed_firmware; + FuIOChannel *io_channel; +}; + +G_DEFINE_TYPE (FuUnifyingRuntime, fu_unifying_runtime, FU_TYPE_UDEV_DEVICE) + +#ifndef HAVE_GUDEV_232 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) +#endif + +static void +fu_unifying_runtime_to_string (FuDevice *device, GString *str) +{ + FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); + g_string_append_printf (str, " SignedFirmware:\t%i\n", self->signed_firmware); +} + +static gboolean +fu_unifying_runtime_enable_notifications (FuUnifyingRuntime *self, GError **error) +{ + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_ID_RECEIVER; + msg->sub_id = HIDPP_SUBID_SET_REGISTER; + msg->function_id = HIDPP_REGISTER_HIDPP_NOTIFICATIONS; + msg->data[0] = 0x00; + msg->data[1] = 0x05; /* Wireless + SoftwarePresent */ + msg->data[2] = 0x00; + msg->hidpp_version = 1; + 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 (!fu_io_channel_shutdown (self->io_channel, error)) + return FALSE; + g_clear_object (&self->io_channel); + return TRUE; +} + +static gboolean +fu_unifying_runtime_poll (FuDevice *device, GError **error) +{ + FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); + const guint timeout = 1; /* ms */ + g_autoptr(GError) error_local = NULL; + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open */ + locker = fu_device_locker_new (self, error); + if (locker == NULL) + return FALSE; + + /* is there any pending data to read */ + msg->hidpp_version = 1; + 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)) { + return TRUE; + } + g_warning ("failed to get pending read: %s", error_local->message); + return TRUE; + } + + /* HID++1.0 error */ + if (!fu_unifying_hidpp_msg_is_error (msg, &error_local)) { + g_warning ("failed to get pending read: %s", error_local->message); + return TRUE; + } + + /* unifying receiver notification */ + if (msg->report_id == HIDPP_REPORT_ID_SHORT) { + switch (msg->sub_id) { + case HIDPP_SUBID_DEVICE_CONNECTION: + case HIDPP_SUBID_DEVICE_DISCONNECTION: + case HIDPP_SUBID_DEVICE_LOCKING_CHANGED: + g_debug ("device connection event, do something"); + break; + case HIDPP_SUBID_LINK_QUALITY: + g_debug ("ignoring link quality message"); + break; + case HIDPP_SUBID_ERROR_MSG: + g_debug ("ignoring link quality message"); + break; + default: + g_debug ("unknown SubID %02x", msg->sub_id); + break; + } + } + return TRUE; +} + +static gboolean +fu_unifying_runtime_open (FuDevice *device, GError **error) +{ + FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + const gchar *devpath = g_udev_device_get_device_file (udev_device); + + /* open, but don't block */ + self->io_channel = fu_io_channel_new_file (devpath, error); + if (self->io_channel == NULL) + return FALSE; + + /* poll for notifications */ + fu_device_set_poll_interval (device, 5000); + + /* success */ + return TRUE; +} + +static gboolean +fu_unifying_runtime_probe (FuUdevDevice *device, GError **error) +{ + FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + guint16 release = 0xffff; + g_autoptr(GUdevDevice) udev_parent = NULL; + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id (device, "usb", error)) + return FALSE; + + /* generate bootloader-specific GUID */ + udev_parent = g_udev_device_get_parent_with_subsystem (udev_device, + "usb", "usb_device"); + if (udev_parent != NULL) { + const gchar *release_str; + release_str = g_udev_device_get_property (udev_parent, "ID_REVISION"); + if (release_str != NULL) + release = g_ascii_strtoull (release_str, NULL, 16); + } + if (release != 0xffff) { + g_autofree gchar *devid2 = NULL; + switch (release &= 0xff00) { + case 0x1200: + /* Nordic */ + devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", + (guint) FU_UNIFYING_DEVICE_VID, + (guint) FU_UNIFYING_DEVICE_PID_BOOTLOADER_NORDIC); + fu_device_add_counterpart_guid (FU_DEVICE (device), devid2); + self->version_bl_major = 0x01; + break; + case 0x2400: + /* Texas */ + devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", + (guint) FU_UNIFYING_DEVICE_VID, + (guint) FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS); + fu_device_add_counterpart_guid (FU_DEVICE (device), devid2); + self->version_bl_major = 0x03; + break; + default: + g_warning ("bootloader release %04x invalid", release); + break; + } + } + return TRUE; +} + +static gboolean +fu_unifying_runtime_setup_internal (FuDevice *device, GError **error) +{ + FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); + guint8 config[10]; + g_autofree gchar *version_fw = NULL; + + /* read all 10 bytes of the version register */ + memset (config, 0x00, sizeof (config)); + for (guint i = 0x01; i < 0x05; i++) { + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + + /* workaround a bug in the 12.01 firmware, which fails with + * INVALID_VALUE when reading MCU1_HW_VERSION */ + if (i == 0x03) + continue; + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_ID_RECEIVER; + msg->sub_id = HIDPP_SUBID_GET_REGISTER; + msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION; + msg->data[0] = i; + msg->hidpp_version = 1; + if (!fu_unifying_hidpp_transfer (self->io_channel, msg, error)) { + g_prefix_error (error, "failed to read device config: "); + return FALSE; + } + memcpy (config + (i * 2), msg->data + 1, 2); + } + + /* get firmware version */ + version_fw = fu_unifying_format_version ("RQR", + config[2], + config[3], + (guint16) config[4] << 8 | + config[5]); + fu_device_set_version (device, version_fw, FWUPD_VERSION_FORMAT_PLAIN); + + /* get bootloader version */ + if (self->version_bl_major > 0) { + g_autofree gchar *version_bl = NULL; + version_bl = fu_unifying_format_version ("BOT", + self->version_bl_major, + config[8], + config[9]); + fu_device_set_version_bootloader (FU_DEVICE (device), version_bl); + + /* is the dongle expecting signed firmware */ + if ((self->version_bl_major == 0x01 && config[8] >= 0x04) || + (self->version_bl_major == 0x03 && config[8] >= 0x02)) { + self->signed_firmware = TRUE; + } + } + + /* enable HID++ notifications */ + if (!fu_unifying_runtime_enable_notifications (self, error)) { + g_prefix_error (error, "failed to enable notifications: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_unifying_runtime_setup (FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + for (guint i = 0; i < 5; i++) { + /* HID++1.0 devices have to sleep to allow Solaar to talk to + * the device first -- we can't use the SwID as this is a + * HID++2.0 feature */ + g_usleep (200*1000); + if (fu_unifying_runtime_setup_internal (device, &error_local)) + return TRUE; + if (!g_error_matches (error_local, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA)) { + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + g_clear_error (&error_local); + } + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; +} + +static gboolean +fu_unifying_runtime_detach (FuDevice *device, GError **error) +{ + FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); + g_autoptr(FuUnifyingHidppMsg) msg = fu_unifying_hidpp_msg_new (); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_ID_RECEIVER; + msg->sub_id = HIDPP_SUBID_SET_REGISTER; + msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE; + msg->data[0] = 'I'; + msg->data[1] = 'C'; + msg->data[2] = 'P'; + msg->hidpp_version = 1; + msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; + 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; + } + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_unifying_runtime_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_unifying_runtime_parent_class)->finalize (object); +} + +static void +fu_unifying_runtime_class_init (FuUnifyingRuntimeClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = fu_unifying_runtime_finalize; + klass_device->open = fu_unifying_runtime_open; + klass_device_udev->probe = fu_unifying_runtime_probe; + klass_device->setup = fu_unifying_runtime_setup; + klass_device->close = fu_unifying_runtime_close; + klass_device->detach = fu_unifying_runtime_detach; + klass_device->poll = fu_unifying_runtime_poll; + klass_device->to_string = fu_unifying_runtime_to_string; +} + +static void +fu_unifying_runtime_init (FuUnifyingRuntime *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_icon (FU_DEVICE (self), "preferences-desktop-keyboard"); + fu_device_set_name (FU_DEVICE (self), "Unifying Receiver"); + fu_device_set_summary (FU_DEVICE (self), "A miniaturised USB wireless receiver"); + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-runtime.h fwupd-1.2.10/plugins/unifying/fu-unifying-runtime.h --- fwupd-1.0.9/plugins/unifying/fu-unifying-runtime.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-runtime.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-udev-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_UNIFYING_RUNTIME (fu_unifying_runtime_get_type ()) +G_DECLARE_FINAL_TYPE (FuUnifyingRuntime, fu_unifying_runtime, FU, UNIFYING_RUNTIME, FuUdevDevice) + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/unifying/fu-unifying-self-test.c fwupd-1.2.10/plugins/unifying/fu-unifying-self-test.c --- fwupd-1.0.9/plugins/unifying/fu-unifying-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/fu-unifying-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-unifying-common.h" + +static void +fu_unifying_common (void) +{ + guint8 u8; + guint16 u16; + g_autofree gchar *ver1 = NULL; + + u8 = fu_unifying_buffer_read_uint8 ("12"); + g_assert_cmpint (u8, ==, 0x12); + u16 = fu_unifying_buffer_read_uint16 ("1234"); + g_assert_cmpint (u16, ==, 0x1234); + + ver1 = fu_unifying_format_version (" A ", 0x87, 0x65, 0x4321); + g_assert_cmpstr (ver1, ==, "A87.65_B4321"); +} + +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 ("/unifying/common", fu_unifying_common); + return g_test_run (); +} diff -Nru fwupd-1.0.9/plugins/unifying/lu-common.c fwupd-1.2.10/plugins/unifying/lu-common.c --- fwupd-1.0.9/plugins/unifying/lu-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "lu-common.h" - -guint8 -lu_buffer_read_uint8 (const gchar *str) -{ - guint64 tmp; - gchar buf[3] = { 0x0, 0x0, 0x0 }; - memcpy (buf, str, 2); - tmp = g_ascii_strtoull (buf, NULL, 16); - return tmp; -} - -guint16 -lu_buffer_read_uint16 (const gchar *str) -{ - guint64 tmp; - gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 }; - memcpy (buf, str, 4); - tmp = g_ascii_strtoull (buf, NULL, 16); - return tmp; -} - -void -lu_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 * -lu_format_version (const gchar *name, guint8 major, guint8 minor, guint16 build) -{ - GString *str = g_string_new (NULL); - for (guint i = 0; i < 3; i++) { - if (g_ascii_isspace (name[i])) - continue; - g_string_append_c (str, name[i]); - } - g_string_append_printf (str, "%02x.%02x_B%04x", major, minor, build); - return g_string_free (str, FALSE); -} - -static gboolean -lu_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 -lu_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 (!lu_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 -lu_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 -lu_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 -Nru fwupd-1.0.9/plugins/unifying/lu-common.h fwupd-1.2.10/plugins/unifying/lu-common.h --- fwupd-1.0.9/plugins/unifying/lu-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_COMMON_H -#define __LU_COMMON_H - -#include - -G_BEGIN_DECLS - -void lu_dump_raw (const gchar *title, - const guint8 *data, - gsize len); - -guint8 lu_buffer_read_uint8 (const gchar *str); -guint16 lu_buffer_read_uint16 (const gchar *str); - -gchar *lu_format_version (const gchar *name, - guint8 major, - guint8 minor, - guint16 build); - -gint lu_nonblock_open (const gchar *filename, - GError **error); -gboolean lu_nonblock_read (gint fd, - guint8 *data, - gsize data_sz, - gsize *data_len, - guint timeout, - GError **error); -gboolean lu_nonblock_write (gint fd, - const guint8 *data, - gsize data_sz, - GError **error); - -G_END_DECLS - -#endif /* __LU_COMMON_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-context.c fwupd-1.2.10/plugins/unifying/lu-context.c --- fwupd-1.0.9/plugins/unifying/lu-context.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-context.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,678 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include -#include - -#include "lu-common.h" -#include "lu-context.h" -#include "lu-device-bootloader-nordic.h" -#include "lu-device-bootloader-texas.h" -#include "lu-device-peripheral.h" -#include "lu-device-runtime.h" -#include "lu-hidpp.h" - -struct _LuContext -{ - GObject parent_instance; - GPtrArray *supported_guids; - GPtrArray *devices; - GHashTable *devices_active; /* LuDevice : 1 */ - GUsbContext *usb_ctx; - GUdevClient *gudev_client; - GHashTable *hash_replug; - gboolean done_coldplug; - GHashTable *hash_devices; - guint poll_id; -}; - -G_DEFINE_TYPE (LuContext, lu_context, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_USB_CONTEXT, - PROP_LAST -}; - -enum { - SIGNAL_ADDED, - SIGNAL_REMOVED, - SIGNAL_LAST -}; - -static guint signals [SIGNAL_LAST] = { 0 }; - -typedef struct { - GMainLoop *loop; - LuDevice *device; - guint timeout_id; -} GUsbContextReplugHelper; - -GPtrArray * -lu_context_get_devices (LuContext *ctx) -{ - /* ensure we have devices */ - if (!ctx->done_coldplug) - lu_context_coldplug (ctx); - return ctx->devices; -} - -static gboolean -lu_context_check_supported (LuContext *ctx, const gchar *guid) -{ - if (ctx->supported_guids == NULL) { - g_debug ("no list of supported GUIDs so assuming supported"); - return TRUE; - } - for (guint i = 0; i < ctx->supported_guids->len; i++) { - const gchar *guid_tmp = g_ptr_array_index (ctx->supported_guids, i); - if (g_strcmp0 (guid, guid_tmp) == 0) - return TRUE; - } - return FALSE; -} - -void -lu_context_set_supported (LuContext *ctx, GPtrArray *supported_guids) -{ - if (ctx->supported_guids != NULL) - g_ptr_array_unref (ctx->supported_guids); - ctx->supported_guids = g_ptr_array_ref (supported_guids); -} - -static void -lu_device_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - LuContext *ctx = LU_CONTEXT (object); - switch (prop_id) { - case PROP_USB_CONTEXT: - g_value_set_object (value, ctx->usb_ctx); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -lu_device_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - LuContext *ctx = LU_CONTEXT (object); - switch (prop_id) { - case PROP_USB_CONTEXT: - ctx->usb_ctx = g_value_dup_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -lu_context_finalize (GObject *object) -{ - LuContext *ctx = LU_CONTEXT (object); - - if (ctx->poll_id != 0) - g_source_remove (ctx->poll_id); - if (ctx->supported_guids != NULL) - g_ptr_array_unref (ctx->supported_guids); - - g_ptr_array_unref (ctx->devices); - g_hash_table_unref (ctx->devices_active); - g_object_unref (ctx->usb_ctx); - g_object_unref (ctx->gudev_client); - g_hash_table_unref (ctx->hash_devices); - g_hash_table_unref (ctx->hash_replug); - G_OBJECT_CLASS (lu_context_parent_class)->finalize (object); -} - -static void -lu_context_class_init (LuContextClass *klass) -{ - GParamSpec *pspec; - GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = lu_context_finalize; - object_class->get_property = lu_device_get_property; - object_class->set_property = lu_device_set_property; - - pspec = g_param_spec_object ("usb-context", NULL, NULL, - G_USB_TYPE_CONTEXT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_USB_CONTEXT, pspec); - - signals [SIGNAL_ADDED] = - g_signal_new ("added", - G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, LU_TYPE_DEVICE); - signals [SIGNAL_REMOVED] = - g_signal_new ("removed", - G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, LU_TYPE_DEVICE); -} - -static void -lu_context_device_flags_notify_cb (GObject *obj, - GParamSpec *pspec, - LuContext *ctx) -{ - LuDevice *device = LU_DEVICE (obj); - if (g_hash_table_lookup (ctx->devices_active, device) != NULL) { - if (!lu_device_has_flag (device, LU_DEVICE_FLAG_ACTIVE)) { - g_debug ("existing device now inactive, sending signal"); - g_signal_emit (ctx, signals[SIGNAL_REMOVED], 0, device); - g_hash_table_remove (ctx->devices_active, device); - } - } else { - if (lu_device_has_flag (device, LU_DEVICE_FLAG_ACTIVE)) { - g_debug ("existing device now active, sending signal"); - g_signal_emit (ctx, signals[SIGNAL_ADDED], 0, device); - g_hash_table_insert (ctx->devices_active, device, GINT_TO_POINTER (1)); - } - } -} - -static void -lu_context_add_device (LuContext *ctx, LuDevice *device) -{ - GUsbContextReplugHelper *replug_helper; - g_autoptr(GError) error = NULL; - - g_return_if_fail (LU_IS_CONTEXT (ctx)); - g_return_if_fail (LU_IS_DEVICE (device)); - - g_debug ("device %s added", fu_device_get_platform_id (FU_DEVICE (device))); - - /* HID++1.0 devices have to sleep to allow Solaar to talk to the device - * first -- we can't use the SwID as this is a HID++2.0 feature */ - if (ctx->done_coldplug && - lu_device_get_hidpp_version (device) <= 1.f) { - g_debug ("waiting for device to settle..."); - g_usleep (G_USEC_PER_SEC); - } - - /* try to open */ - if (!lu_device_open (device, &error)) { - if (g_error_matches (error, - G_IO_ERROR, - G_IO_ERROR_HOST_UNREACHABLE)) { - g_debug ("could not open: %s", error->message); - } else { - g_warning ("failed to open: %s", error->message); - } - return; - } - - /* emit */ - g_ptr_array_add (ctx->devices, g_object_ref (device)); - if (lu_device_has_flag (device, LU_DEVICE_FLAG_ACTIVE)) { - g_signal_emit (ctx, signals[SIGNAL_ADDED], 0, device); - g_hash_table_insert (ctx->devices_active, device, GINT_TO_POINTER (1)); - } - g_signal_connect (device, "notify::flags", - G_CALLBACK (lu_context_device_flags_notify_cb), ctx); - - /* if we're waiting for replug, quit the loop */ - replug_helper = g_hash_table_lookup (ctx->hash_replug, - fu_device_get_platform_id (FU_DEVICE (device))); - if (replug_helper != NULL) { - g_debug ("%s is in replug, quitting loop", - fu_device_get_platform_id (FU_DEVICE (device))); - g_main_loop_quit (replug_helper->loop); - } - -} - -static void -lu_context_remove_device (LuContext *ctx, LuDevice *device) -{ - g_return_if_fail (LU_IS_CONTEXT (ctx)); - g_return_if_fail (LU_IS_DEVICE (device)); - - g_debug ("device %s removed", fu_device_get_platform_id (FU_DEVICE (device))); - - /* no longer valid */ - g_object_set (device, - "usb-device", NULL, - "udev-device", NULL, - NULL); - - if (lu_device_has_flag (device, LU_DEVICE_FLAG_ACTIVE)) - g_signal_emit (ctx, signals[SIGNAL_REMOVED], 0, device); - g_ptr_array_remove (ctx->devices, 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 - -static const gchar * -lu_context_get_platform_id_for_udev_device (GUdevDevice *udev_device) -{ - g_autoptr(GUdevDevice) udev_device1 = NULL; - udev_device1 = g_udev_device_get_parent_with_subsystem (udev_device, - "usb", "usb_device"); - if (udev_device1 == NULL) - return NULL; - return g_udev_device_get_sysfs_path (udev_device1); -} - -static void -lu_context_add_udev_device (LuContext *ctx, GUdevDevice *udev_device) -{ - const gchar *val; - const gchar *platform_id; - guint16 pid; - guint16 vid; - g_autofree gchar *devid = NULL; - g_autoptr(GUdevDevice) udev_parent = NULL; - g_autoptr(LuDevice) device = NULL; - - g_return_if_fail (LU_IS_CONTEXT (ctx)); - - g_debug ("UDEV add %s = %s", - g_udev_device_get_device_file (udev_device), - g_udev_device_get_sysfs_path (udev_device)); - - /* check the vid:pid from property HID_ID=0003:0000046D:0000C52B */ - udev_parent = g_udev_device_get_parent (udev_device); - val = g_udev_device_get_property (udev_parent, "HID_ID"); - if (val == NULL) { - g_debug ("no HID_ID, skipping"); - return; - } - if (strlen (val) != 22) { - g_warning ("property HID_ID invalid '%s', skipping", val); - return; - } - - /* is logitech */ - vid = lu_buffer_read_uint16 (val + 10); - if (vid != LU_DEVICE_VID) { - g_debug ("not a matching vid: %04x", vid); - return; - } - - /* is unifying runtime */ - pid = lu_buffer_read_uint16 (val + 18); - if (pid == LU_DEVICE_PID_RUNTIME) { - platform_id = lu_context_get_platform_id_for_udev_device (udev_device); - device = g_object_new (LU_TYPE_DEVICE_RUNTIME, - "kind", LU_DEVICE_KIND_RUNTIME, - "flags", LU_DEVICE_FLAG_ACTIVE | - LU_DEVICE_FLAG_REQUIRES_DETACH | - LU_DEVICE_FLAG_DETACH_WILL_REPLUG, - "platform-id", platform_id, - "udev-device", udev_device, - "hidpp-id", HIDPP_DEVICE_ID_RECEIVER, - NULL); - g_hash_table_insert (ctx->hash_devices, - g_strdup (fu_device_get_platform_id (FU_DEVICE (device))), - g_object_ref (device)); - lu_context_add_device (ctx, device); - return; - } - - /* is unifying bootloader */ - if (pid == LU_DEVICE_PID_BOOTLOADER_NORDIC || - pid == LU_DEVICE_PID_BOOTLOADER_NORDIC_PICO || - pid == LU_DEVICE_PID_BOOTLOADER_TEXAS || - pid == LU_DEVICE_PID_BOOTLOADER_TEXAS_PICO) { - g_debug ("ignoring bootloader in HID mode"); - return; - } - - /* is peripheral */ - platform_id = g_udev_device_get_sysfs_path (udev_device); - device = g_object_new (LU_TYPE_DEVICE_PERIPHERAL, - "kind", LU_DEVICE_KIND_PERIPHERAL, - "platform-id", platform_id, - "udev-device", udev_device, - NULL); - val = g_udev_device_get_property (udev_parent, "HID_NAME"); - if (val != NULL) { - if (g_str_has_prefix (val, "Logitech ")) - val += 9; - fu_device_set_name (FU_DEVICE (device), val); - } - - /* generate GUID */ - devid = g_strdup_printf ("UFY\\VID_%04X&PID_%04X", vid, pid); - fu_device_add_guid (FU_DEVICE (device), devid); - if (!lu_context_check_supported (ctx, fu_device_get_guid_default (FU_DEVICE (device)))) { - g_debug ("%s not supported, so ignoring device", devid); - return; - } - g_hash_table_insert (ctx->hash_devices, - g_strdup (fu_device_get_platform_id (FU_DEVICE (device))), - g_object_ref (device)); - lu_context_add_device (ctx, device); -} - -static gboolean -g_usb_context_replug_timeout_cb (gpointer user_data) -{ - GUsbContextReplugHelper *replug_helper = (GUsbContextReplugHelper *) user_data; - replug_helper->timeout_id = 0; - g_main_loop_quit (replug_helper->loop); - return FALSE; -} - -static void -g_usb_context_replug_helper_free (GUsbContextReplugHelper *replug_helper) -{ - if (replug_helper->timeout_id != 0) - g_source_remove (replug_helper->timeout_id); - g_main_loop_unref (replug_helper->loop); - g_object_unref (replug_helper->device); - g_free (replug_helper); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUsbContextReplugHelper, g_usb_context_replug_helper_free); -#pragma clang diagnostic pop - -gboolean -lu_context_wait_for_replug (LuContext *ctx, - LuDevice *device, - guint timeout_ms, - GError **error) -{ - g_autoptr(GUsbContextReplugHelper) replug_helper = NULL; - const gchar *platform_id; - - g_return_val_if_fail (LU_IS_CONTEXT (ctx), FALSE); - g_return_val_if_fail (LU_IS_DEVICE (device), FALSE); - - /* create a helper */ - replug_helper = g_new0 (GUsbContextReplugHelper, 1); - replug_helper->device = g_object_ref (device); - replug_helper->loop = g_main_loop_new (NULL, FALSE); - replug_helper->timeout_id = g_timeout_add (timeout_ms, - g_usb_context_replug_timeout_cb, - replug_helper); - - /* register */ - platform_id = fu_device_get_platform_id (FU_DEVICE (device)); - g_hash_table_insert (ctx->hash_replug, - g_strdup (platform_id), replug_helper); - - /* wait for timeout, or replug */ - g_main_loop_run (replug_helper->loop); - - /* unregister */ - g_hash_table_remove (ctx->hash_replug, platform_id); - - /* so we timed out; emit the removal now */ - if (replug_helper->timeout_id == 0) { - g_set_error_literal (error, - G_USB_CONTEXT_ERROR, - G_USB_CONTEXT_ERROR_INTERNAL, - "request timed out"); - return FALSE; - } - return TRUE; -} - -static void -lu_context_remove_udev_device (LuContext *ctx, GUdevDevice *udev_device) -{ - /* look for this udev_device in all the objects */ - for (guint i = 0; i < ctx->devices->len; i++) { - LuDevice *device = g_ptr_array_index (ctx->devices, i); - GUdevDevice *udev_device_tmp = lu_device_get_udev_device (device); - if (udev_device_tmp == NULL) - continue; - if (g_strcmp0 (g_udev_device_get_sysfs_path (udev_device_tmp), - g_udev_device_get_sysfs_path (udev_device)) == 0) { - lu_context_remove_device (ctx, device); - break; - } - } -} - -static gboolean -lu_context_poll_cb (gpointer user_data) -{ - LuContext *ctx = LU_CONTEXT (user_data); - - /* do not poll when we're waiting for device replug */ - if (g_hash_table_size (ctx->hash_replug) > 0) { - g_debug ("not polling device as replug in process"); - return TRUE; - } - - for (guint i = 0; i < ctx->devices->len; i++) { - LuDevice *device = g_ptr_array_index (ctx->devices, i); - g_autoptr(GError) error = NULL; - if (!lu_device_open (device, &error)) { - g_debug ("failed to open %s: %s", - fu_device_get_platform_id (FU_DEVICE (device)), - error->message); - continue; - } - if (!lu_device_poll (device, &error)) { - g_debug ("failed to probe %s: %s", - fu_device_get_platform_id (FU_DEVICE (device)), - error->message); - continue; - } - } - return TRUE; -} - -void -lu_context_set_poll_interval (LuContext *ctx, guint poll_interval) -{ - /* enable or change */ - if (poll_interval > 0) { - if (ctx->poll_id > 0) - g_source_remove (ctx->poll_id); - ctx->poll_id = g_timeout_add (poll_interval, - lu_context_poll_cb, - ctx); - return; - } - - /* disable */ - if (poll_interval == 0 && ctx->poll_id != 0) { - g_source_remove (ctx->poll_id); - ctx->poll_id = 0; - return; - } -} - -static void -lu_context_udev_uevent_cb (GUdevClient *gudev_client, - const gchar *action, - GUdevDevice *udev_device, - LuContext *ctx) -{ - if (g_strcmp0 (action, "remove") == 0) { - lu_context_remove_udev_device (ctx, udev_device); - return; - } - if (g_strcmp0 (action, "add") == 0) { - lu_context_add_udev_device (ctx, udev_device); - return; - } -} - -static void -lu_context_init (LuContext *ctx) -{ - const gchar *subsystems[] = { "hidraw", NULL }; - ctx->gudev_client = g_udev_client_new (subsystems); - g_signal_connect (ctx->gudev_client, "uevent", - G_CALLBACK (lu_context_udev_uevent_cb), ctx); - ctx->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - ctx->devices_active = g_hash_table_new (g_direct_hash, g_direct_equal); - ctx->hash_devices = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_object_unref); - ctx->hash_replug = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); -} - -void -lu_context_coldplug (LuContext *ctx) -{ - g_autoptr(GList) devices = NULL; - - g_return_if_fail (LU_IS_CONTEXT (ctx)); - - if (ctx->done_coldplug) - return; - - /* coldplug hidraw devices */ - devices = g_udev_client_query_by_subsystem (ctx->gudev_client, "hidraw"); - for (GList *l = devices; l != NULL; l = l->next) { - GUdevDevice *udev_device = G_UDEV_DEVICE (l->data); - lu_context_add_udev_device (ctx, udev_device); - g_object_unref (udev_device); - } - - /* done */ - ctx->done_coldplug = TRUE; -} - -LuDevice * -lu_context_find_by_platform_id (LuContext *ctx, const gchar *platform_id, GError **error) -{ - g_return_val_if_fail (LU_IS_CONTEXT (ctx), NULL); - g_return_val_if_fail (platform_id != NULL, NULL); - - /* ensure we have devices */ - if (!ctx->done_coldplug) - lu_context_coldplug (ctx); - - for (guint i = 0; i < ctx->devices->len; i++) { - LuDevice *device = g_ptr_array_index (ctx->devices, i); - if (g_strcmp0 (fu_device_get_platform_id (FU_DEVICE (device)), platform_id) == 0) - return g_object_ref (device); - } - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "not found %s", platform_id); - return NULL; -} - -static void -lu_context_usb_device_added_cb (GUsbContext *usb_ctx, - GUsbDevice *usb_device, - LuContext *ctx) -{ - g_return_if_fail (LU_IS_CONTEXT (ctx)); - - /* logitech */ - if (g_usb_device_get_vid (usb_device) != LU_DEVICE_VID) - return; - - g_debug ("USB add %s", g_usb_device_get_platform_id (usb_device)); - - /* nordic, in bootloader mode */ - if (g_usb_device_get_pid (usb_device) == LU_DEVICE_PID_BOOTLOADER_NORDIC || - g_usb_device_get_pid (usb_device) == LU_DEVICE_PID_BOOTLOADER_NORDIC_PICO) { - g_autoptr(LuDevice) device = NULL; - device = g_object_new (LU_TYPE_DEVICE_BOOTLOADER_NORDIC, - "kind", LU_DEVICE_KIND_BOOTLOADER_NORDIC, - "flags", LU_DEVICE_FLAG_ACTIVE | - LU_DEVICE_FLAG_REQUIRES_ATTACH | - LU_DEVICE_FLAG_ATTACH_WILL_REPLUG, - "hidpp-id", HIDPP_DEVICE_ID_RECEIVER, - "usb-device", usb_device, - NULL); - lu_context_add_device (ctx, device); - return; - } - - /* texas, in bootloader mode */ - if (g_usb_device_get_pid (usb_device) == LU_DEVICE_PID_BOOTLOADER_TEXAS || - g_usb_device_get_pid (usb_device) == LU_DEVICE_PID_BOOTLOADER_TEXAS_PICO) { - g_autoptr(LuDevice) device = NULL; - device = g_object_new (LU_TYPE_DEVICE_BOOTLOADER_TEXAS, - "kind", LU_DEVICE_KIND_BOOTLOADER_TEXAS, - "flags", LU_DEVICE_FLAG_ACTIVE | - LU_DEVICE_FLAG_REQUIRES_ATTACH | - LU_DEVICE_FLAG_ATTACH_WILL_REPLUG, - "hidpp-id", HIDPP_DEVICE_ID_RECEIVER, - "usb-device", usb_device, - NULL); - lu_context_add_device (ctx, device); - return; - } -} - -static void -lu_context_usb_device_removed_cb (GUsbContext *usb_ctx, - GUsbDevice *usb_device, - LuContext *ctx) -{ - g_return_if_fail (LU_IS_CONTEXT (ctx)); - - /* logitech */ - if (g_usb_device_get_vid (usb_device) != LU_DEVICE_VID) - return; - - /* look for this usb_device in all the objects */ - for (guint i = 0; i < ctx->devices->len; i++) { - LuDevice *device = g_ptr_array_index (ctx->devices, i); - if (lu_device_get_usb_device (device) == usb_device) { - lu_context_remove_device (ctx, device); - break; - } - } -} - -static void -lu_context_init_real (LuContext *ctx) -{ - g_signal_connect (ctx->usb_ctx, "device-added", - G_CALLBACK (lu_context_usb_device_added_cb), - ctx); - g_signal_connect (ctx->usb_ctx, "device-removed", - G_CALLBACK (lu_context_usb_device_removed_cb), - ctx); -} - -LuContext * -lu_context_new (GError **error) -{ - LuContext *ctx = NULL; - g_autoptr(GUsbContext) usb_ctx = NULL; - - usb_ctx = g_usb_context_new (error); - if (usb_ctx == NULL) - return NULL; - ctx = g_object_new (LU_TYPE_CONTEXT, - "usb-context", usb_ctx, - NULL); - lu_context_init_real (ctx); - g_usb_context_enumerate (ctx->usb_ctx); - return ctx; -} - -LuContext * -lu_context_new_full (GUsbContext *usb_ctx) -{ - LuContext *ctx = NULL; - ctx = g_object_new (LU_TYPE_CONTEXT, - "usb-context", usb_ctx, - NULL); - lu_context_init_real (ctx); - return ctx; -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-context.h fwupd-1.2.10/plugins/unifying/lu-context.h --- fwupd-1.0.9/plugins/unifying/lu-context.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-context.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_CONTEXT_H -#define __LU_CONTEXT_H - -#include -#include - -#include "lu-device.h" - -G_BEGIN_DECLS - -#define LU_TYPE_CONTEXT (lu_context_get_type ()) -G_DECLARE_FINAL_TYPE (LuContext, lu_context, LU, CONTEXT, GObject) - -GPtrArray *lu_context_get_devices (LuContext *ctx); -LuDevice *lu_context_find_by_platform_id (LuContext *ctx, - const gchar *platform_id, - GError **error); -void lu_context_coldplug (LuContext *ctx); -void lu_context_set_poll_interval (LuContext *ctx, - guint poll_interval); -void lu_context_set_supported (LuContext *ctx, - GPtrArray *supported_guids); -gboolean lu_context_wait_for_replug (LuContext *ctx, - LuDevice *device, - guint timeout_ms, - GError **error); - -LuContext *lu_context_new (GError **error); -LuContext *lu_context_new_full (GUsbContext *usb_ctx); - -G_END_DECLS - -#endif /* __LU_CONTEXT_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-bootloader.c fwupd-1.2.10/plugins/unifying/lu-device-bootloader.c --- fwupd-1.0.9/plugins/unifying/lu-device-bootloader.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-bootloader.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,412 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "lu-common.h" -#include "lu-device-bootloader.h" -#include "lu-hidpp.h" - -typedef struct -{ - guint16 flash_addr_lo; - guint16 flash_addr_hi; - guint16 flash_blocksize; -} LuDeviceBootloaderPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (LuDeviceBootloader, lu_device_bootloader, LU_TYPE_DEVICE) - -#define GET_PRIVATE(o) (lu_device_bootloader_get_instance_private (o)) - -LuDeviceBootloaderRequest * -lu_device_bootloader_request_new (void) -{ - LuDeviceBootloaderRequest *req = g_new0 (LuDeviceBootloaderRequest, 1); - return req; -} - -GPtrArray * -lu_device_bootloader_parse_requests (LuDevice *device, GBytes *fw, GError **error) -{ - const gchar *tmp; - g_auto(GStrv) lines = NULL; - g_autoptr(GPtrArray) reqs = NULL; - guint32 last_addr = 0; - - reqs = g_ptr_array_new_with_free_func (g_free); - tmp = g_bytes_get_data (fw, NULL); - lines = g_strsplit_set (tmp, "\n\r", -1); - for (guint i = 0; lines[i] != NULL; i++) { - g_autoptr(LuDeviceBootloaderRequest) payload = NULL; - guint8 rec_type = 0x00; - - /* skip empty lines */ - tmp = lines[i]; - if (strlen (tmp) < 5) - continue; - - payload = lu_device_bootloader_request_new (); - payload->len = lu_buffer_read_uint8 (tmp + 0x01); - if (payload->len > 28) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "firmware data invalid: too large %u bytes", - payload->len); - return NULL; - } - payload->addr = ((guint16) lu_buffer_read_uint8 (tmp + 0x03)) << 8; - payload->addr |= lu_buffer_read_uint8 (tmp + 0x05); - - rec_type = lu_buffer_read_uint8 (tmp + 0x07); - - /* record type of 0xFD indicates signature data */ - if (rec_type == 0xFD) { - payload->cmd = LU_DEVICE_BOOTLOADER_CMD_WRITE_SIGNATURE; - } else { - payload->cmd = LU_DEVICE_BOOTLOADER_CMD_WRITE_RAM_BUFFER; - } - - /* read the data, but skip the checksum byte */ - for (guint j = 0; j < payload->len; j++) { - const gchar *ptr = tmp + 0x09 + (j * 2); - if (ptr[0] == '\0') { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "firmware data invalid: expected %u bytes", - payload->len); - return NULL; - } - payload->data[j] = lu_buffer_read_uint8 (ptr); - } - - /* no need to bound check signature addresses */ - if (payload->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_SIGNATURE) { - g_ptr_array_add (reqs, g_steal_pointer (&payload)); - continue; - } - - /* skip the bootloader */ - if (payload->addr > lu_device_bootloader_get_addr_hi (device)) { - g_debug ("skipping write @ %04x", payload->addr); - continue; - } - - /* skip the header */ - if (payload->addr < lu_device_bootloader_get_addr_lo (device)) { - g_debug ("skipping write @ %04x", payload->addr); - continue; - } - - /* make sure firmware addresses only go up */ - if (payload->addr < last_addr) { - g_debug ("skipping write @ %04x", payload->addr); - continue; - } - last_addr = payload->addr; - - /* pending */ - g_ptr_array_add (reqs, g_steal_pointer (&payload)); - } - if (reqs->len == 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "firmware data invalid: no payloads found"); - return NULL; - } - return g_steal_pointer (&reqs); -} - -guint16 -lu_device_bootloader_get_addr_lo (LuDevice *device) -{ - LuDeviceBootloader *device_bootloader = LU_DEVICE_BOOTLOADER (device); - LuDeviceBootloaderPrivate *priv = GET_PRIVATE (device_bootloader); - g_return_val_if_fail (LU_IS_DEVICE (device), 0x0000); - return priv->flash_addr_lo; -} - -guint16 -lu_device_bootloader_get_addr_hi (LuDevice *device) -{ - LuDeviceBootloader *device_bootloader = LU_DEVICE_BOOTLOADER (device); - LuDeviceBootloaderPrivate *priv = GET_PRIVATE (device_bootloader); - g_return_val_if_fail (LU_IS_DEVICE (device), 0x0000); - return priv->flash_addr_hi; -} - -void -lu_device_bootloader_set_addr_lo (LuDevice *device, guint16 addr) -{ - LuDeviceBootloader *device_bootloader = LU_DEVICE_BOOTLOADER (device); - LuDeviceBootloaderPrivate *priv = GET_PRIVATE (device_bootloader); - priv->flash_addr_lo = addr; -} - -void -lu_device_bootloader_set_addr_hi (LuDevice *device, guint16 addr) -{ - LuDeviceBootloader *device_bootloader = LU_DEVICE_BOOTLOADER (device); - LuDeviceBootloaderPrivate *priv = GET_PRIVATE (device_bootloader); - priv->flash_addr_hi = addr; -} - -guint16 -lu_device_bootloader_get_blocksize (LuDevice *device) -{ - LuDeviceBootloader *device_bootloader = LU_DEVICE_BOOTLOADER (device); - LuDeviceBootloaderPrivate *priv = GET_PRIVATE (device_bootloader); - g_return_val_if_fail (LU_IS_DEVICE (device), 0x0000); - return priv->flash_blocksize; -} - -static gboolean -lu_device_bootloader_attach (LuDevice *device, GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_REBOOT; - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to attach back to runtime: "); - return FALSE; - } - return TRUE; -} - -static guint16 -cd_buffer_read_uint16_be (const guint8 *buffer) -{ - guint16 tmp; - memcpy (&tmp, buffer, sizeof(tmp)); - return GUINT16_FROM_BE (tmp); -} - -static gboolean -lu_device_bootloader_open (LuDevice *device, GError **error) -{ - LuDeviceBootloader *device_bootloader = LU_DEVICE_BOOTLOADER (device); - LuDeviceBootloaderPrivate *priv = GET_PRIVATE (device_bootloader); - g_autofree gchar *name = NULL; - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - - /* generate name */ - name = g_strdup_printf ("Unifying [%s]", - lu_device_kind_to_string (lu_device_get_kind (device))); - fu_device_set_name (FU_DEVICE (device), name); - - /* we can flash this */ - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); - - /* get memory map */ - req->cmd = LU_DEVICE_BOOTLOADER_CMD_GET_MEMINFO; - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to get meminfo: "); - return FALSE; - } - if (req->len != 0x06) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to get meminfo: invalid size %02x", - req->len); - return FALSE; - } - - /* parse values */ - priv->flash_addr_lo = cd_buffer_read_uint16_be (req->data + 0); - priv->flash_addr_hi = cd_buffer_read_uint16_be (req->data + 2); - priv->flash_blocksize = cd_buffer_read_uint16_be (req->data + 4); - return TRUE; -} - -static gchar * -lu_device_bootloader_get_bl_version (LuDevice *device, GError **error) -{ - guint16 build; - - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_GET_BL_VERSION; - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to get firmware version: "); - return NULL; - } - - /* BOTxx.yy_Bzzzz - * 012345678901234 */ - build = (guint16) lu_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; - build += lu_buffer_read_uint8 ((const gchar *) req->data + 12); - return lu_format_version ("BOT", - lu_buffer_read_uint8 ((const gchar *) req->data + 3), - lu_buffer_read_uint8 ((const gchar *) req->data + 6), - build); -} - -static gboolean -lu_device_bootloader_probe (LuDevice *device, GError **error) -{ - LuDeviceBootloaderClass *klass = LU_DEVICE_BOOTLOADER_GET_CLASS (device); - g_autofree gchar *version_bl = NULL; - - /* get bootloader version */ - version_bl = lu_device_bootloader_get_bl_version (device, error); - if (version_bl == NULL) - return FALSE; - fu_device_set_version_bootloader (FU_DEVICE (device), version_bl); - - /* subclassed further */ - if (klass->probe != NULL) - return klass->probe (device, error); - return TRUE; -} - -static gboolean -lu_device_bootloader_close (LuDevice *device, GError **error) -{ - GUsbDevice *usb_device = lu_device_get_usb_device (device); - if (usb_device != NULL) { - if (!g_usb_device_release_interface (usb_device, 0x00, - G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, - error)) { - return FALSE; - } - } - return TRUE; -} - -gboolean -lu_device_bootloader_request (LuDevice *device, - LuDeviceBootloaderRequest *req, - GError **error) -{ - GUsbDevice *usb_device = lu_device_get_usb_device (device); - gsize actual_length = 0; - guint8 buf_request[32]; - guint8 buf_response[32]; - - /* build packet */ - memset (buf_request, 0x00, sizeof (buf_request)); - buf_request[0x00] = req->cmd; - buf_request[0x01] = req->addr >> 8; - buf_request[0x02] = req->addr & 0xff; - buf_request[0x03] = req->len; - memcpy (buf_request + 0x04, req->data, 28); - - /* send request */ - lu_dump_raw ("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, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - LU_REQUEST_SET_REPORT, - 0x0200, 0x0000, - buf_request, - sizeof (buf_request), - &actual_length, - LU_DEVICE_TIMEOUT_MS, - NULL, - error)) { - g_prefix_error (error, "failed to send data: "); - return FALSE; - } - } - - /* no response required when rebooting */ - if (usb_device != NULL && - req->cmd == LU_DEVICE_BOOTLOADER_CMD_REBOOT) { - g_autoptr(GError) error_ignore = NULL; - if (!g_usb_device_interrupt_transfer (usb_device, - LU_DEVICE_EP1, - buf_response, - sizeof (buf_response), - &actual_length, - LU_DEVICE_TIMEOUT_MS, - NULL, - &error_ignore)) { - g_debug ("ignoring: %s", error_ignore->message); - } else { - lu_dump_raw ("device->host", buf_response, actual_length); - } - return TRUE; - } - - /* get response */ - memset (buf_response, 0x00, sizeof (buf_response)); - if (usb_device != NULL) { - if (!g_usb_device_interrupt_transfer (usb_device, - LU_DEVICE_EP1, - buf_response, - sizeof (buf_response), - &actual_length, - LU_DEVICE_TIMEOUT_MS, - NULL, - error)) { - g_prefix_error (error, "failed to get data: "); - return FALSE; - } - } else { - /* emulated */ - buf_response[0] = buf_request[0]; - if (buf_response[0] == LU_DEVICE_BOOTLOADER_CMD_GET_MEMINFO) { - buf_response[3] = 0x06; /* len */ - buf_response[4] = 0x40; /* lo MSB */ - buf_response[5] = 0x00; /* lo LSB */ - buf_response[6] = 0x6b; /* hi MSB */ - buf_response[7] = 0xff; /* hi LSB */ - buf_response[8] = 0x00; /* bs MSB */ - buf_response[9] = 0x80; /* bs LSB */ - } - actual_length = sizeof (buf_response); - } - lu_dump_raw ("device->host", buf_response, actual_length); - - /* parse response */ - if ((buf_response[0x00] & 0xf0) != req->cmd) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "invalid command response of %02x, expected %02x", - buf_response[0x00], req->cmd); - return FALSE; - } - req->cmd = buf_response[0x00]; - req->addr = ((guint16) buf_response[0x01] << 8) + buf_response[0x02]; - req->len = buf_response[0x03]; - if (req->len > 28) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "invalid data size of %02x", req->len); - return FALSE; - } - memset (req->data, 0x00, 28); - if (req->len > 0) - memcpy (req->data, buf_response + 0x04, req->len); - return TRUE; -} - -static void -lu_device_bootloader_init (LuDeviceBootloader *device) -{ - /* FIXME: we need something better */ - fu_device_add_icon (FU_DEVICE (device), "preferences-desktop-keyboard"); - fu_device_set_summary (FU_DEVICE (device), "A miniaturised USB wireless receiver (bootloader)"); - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); -} - -static void -lu_device_bootloader_class_init (LuDeviceBootloaderClass *klass) -{ - LuDeviceClass *klass_device = LU_DEVICE_CLASS (klass); - klass_device->attach = lu_device_bootloader_attach; - klass_device->open = lu_device_bootloader_open; - klass_device->close = lu_device_bootloader_close; - klass_device->probe = lu_device_bootloader_probe; -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-bootloader.h fwupd-1.2.10/plugins/unifying/lu-device-bootloader.h --- fwupd-1.0.9/plugins/unifying/lu-device-bootloader.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-bootloader.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_DEVICE_BOOTLOADER_H -#define __LU_DEVICE_BOOTLOADER_H - -#include - -#include "lu-device.h" - -G_BEGIN_DECLS - -#define LU_TYPE_DEVICE_BOOTLOADER (lu_device_bootloader_get_type ()) -G_DECLARE_DERIVABLE_TYPE (LuDeviceBootloader, lu_device_bootloader, LU, DEVICE_BOOTLOADER, LuDevice) - -struct _LuDeviceBootloaderClass -{ - LuDeviceClass parent_class; - gboolean (*probe) (LuDevice *device, - GError **error); -}; - -typedef enum { - LU_DEVICE_BOOTLOADER_CMD_GENERAL_ERROR = 0x01, - LU_DEVICE_BOOTLOADER_CMD_READ = 0x10, - LU_DEVICE_BOOTLOADER_CMD_WRITE = 0x20, - LU_DEVICE_BOOTLOADER_CMD_WRITE_INVALID_ADDR = 0x21, - LU_DEVICE_BOOTLOADER_CMD_WRITE_VERIFY_FAIL = 0x22, - LU_DEVICE_BOOTLOADER_CMD_WRITE_NONZERO_START = 0x23, - LU_DEVICE_BOOTLOADER_CMD_WRITE_INVALID_CRC = 0x24, - LU_DEVICE_BOOTLOADER_CMD_ERASE_PAGE = 0x30, - LU_DEVICE_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR = 0x31, - LU_DEVICE_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START = 0x33, - LU_DEVICE_BOOTLOADER_CMD_GET_HW_PLATFORM_ID = 0x40, - LU_DEVICE_BOOTLOADER_CMD_GET_FW_VERSION = 0x50, - LU_DEVICE_BOOTLOADER_CMD_GET_CHECKSUM = 0x60, - LU_DEVICE_BOOTLOADER_CMD_REBOOT = 0x70, - LU_DEVICE_BOOTLOADER_CMD_GET_MEMINFO = 0x80, - LU_DEVICE_BOOTLOADER_CMD_GET_BL_VERSION = 0x90, - LU_DEVICE_BOOTLOADER_CMD_GET_INIT_FW_VERSION = 0xa0, - LU_DEVICE_BOOTLOADER_CMD_READ_SIGNATURE = 0xb0, - LU_DEVICE_BOOTLOADER_CMD_WRITE_RAM_BUFFER = 0xc0, - LU_DEVICE_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR = 0xc1, - LU_DEVICE_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW = 0xc2, - LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM = 0xd0, - LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR = 0xd1, - LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC = 0xd2, - LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID = 0xd3, - LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER = 0xd4, - LU_DEVICE_BOOTLOADER_CMD_WRITE_SIGNATURE = 0xe0, - LU_DEVICE_BOOTLOADER_CMD_LAST -} LuDeviceBootloaderCmd; - -/* packet to and from device */ -typedef struct __attribute__((packed)) { - guint8 cmd; - guint16 addr; - guint8 len; - guint8 data[28]; -} LuDeviceBootloaderRequest; - -LuDeviceBootloaderRequest *lu_device_bootloader_request_new (void); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(LuDeviceBootloaderRequest, g_free); -#pragma clang diagnostic pop - -GPtrArray *lu_device_bootloader_parse_requests (LuDevice *device, - GBytes *fw, - GError **error); -gboolean lu_device_bootloader_request (LuDevice *device, - LuDeviceBootloaderRequest *req, - GError **error); - -guint16 lu_device_bootloader_get_addr_lo (LuDevice *device); -guint16 lu_device_bootloader_get_addr_hi (LuDevice *device); -void lu_device_bootloader_set_addr_lo (LuDevice *device, - guint16 addr); -void lu_device_bootloader_set_addr_hi (LuDevice *device, - guint16 addr); -guint16 lu_device_bootloader_get_blocksize (LuDevice *device); - -G_END_DECLS - -#endif /* __LU_DEVICE_BOOTLOADER_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-bootloader-nordic.c fwupd-1.2.10/plugins/unifying/lu-device-bootloader-nordic.c --- fwupd-1.0.9/plugins/unifying/lu-device-bootloader-nordic.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-bootloader-nordic.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,274 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "lu-common.h" -#include "lu-device-bootloader-nordic.h" - -struct _LuDeviceBootloaderNordic -{ - LuDeviceBootloader parent_instance; -}; - -G_DEFINE_TYPE (LuDeviceBootloaderNordic, lu_device_bootloader_nordic, LU_TYPE_DEVICE_BOOTLOADER) - -static gchar * -lu_device_bootloader_nordic_get_hw_platform_id (LuDevice *device, GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_GET_HW_PLATFORM_ID; - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to get HW ID: "); - return NULL; - } - return g_strndup ((const gchar *) req->data, req->len); -} - -static gchar * -lu_device_bootloader_nordic_get_fw_version (LuDevice *device, GError **error) -{ - guint16 micro; - - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_GET_FW_VERSION; - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to get firmware version: "); - return NULL; - } - - /* RRRxx.yy_Bzzzz - * 012345678901234*/ - micro = (guint16) lu_buffer_read_uint8 ((const gchar *) req->data + 10) << 8; - micro += lu_buffer_read_uint8 ((const gchar *) req->data + 12); - return lu_format_version ("RQR", - lu_buffer_read_uint8 ((const gchar *) req->data + 3), - lu_buffer_read_uint8 ((const gchar *) req->data + 6), - micro); -} - -static gboolean -lu_device_bootloader_nordic_probe (LuDevice *device, GError **error) -{ - g_autofree gchar *hw_platform_id = NULL; - g_autofree gchar *version_fw = NULL; - g_autoptr(GError) error_local = NULL; - - /* get MCU */ - hw_platform_id = lu_device_bootloader_nordic_get_hw_platform_id (device, error); - if (hw_platform_id == NULL) - return FALSE; - g_debug ("hw-platform-id=%s", hw_platform_id); - - /* get firmware version, which is not fatal */ - version_fw = lu_device_bootloader_nordic_get_fw_version (device, &error_local); - if (version_fw == NULL) { - g_warning ("failed to get firmware version: %s", - error_local->message); - fu_device_set_version (FU_DEVICE (device), "RQR12.00_B0000"); - } else { - fu_device_set_version (FU_DEVICE (device), version_fw); - } - - return TRUE; -} - -static gboolean -lu_device_bootloader_nordic_write_signature (LuDevice *device, - guint16 addr, guint8 len, const guint8 *data, - GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new(); - req->cmd = 0xC0; - req->addr = addr; - req->len = len; - memcpy (req->data, data, req->len); - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to write sig @0x%02x: ", addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write @%04x: signature is too big", - addr); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_bootloader_nordic_write (LuDevice *device, - guint16 addr, guint8 len, const guint8 *data, - GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_WRITE; - req->addr = addr; - req->len = len; - if (req->len > 28) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write @%04x: data length too large %02x", - addr, req->len); - return FALSE; - } - memcpy (req->data, data, req->len); - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to transfer fw @0x%02x: ", addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_INVALID_ADDR) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write @%04x: invalid address", - addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_VERIFY_FAIL) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write @%04x: failed to verify flash content", - addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_NONZERO_START) { - g_debug ("wrote %d bytes at address %04x, value %02x", req->len, - req->addr, req->data[0]); - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write @%04x: only 1 byte write of 0xff supported", - addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_INVALID_CRC) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write @%04x: invalid CRC", - addr); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_bootloader_nordic_erase (LuDevice *device, guint16 addr, GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_ERASE_PAGE; - req->addr = addr; - req->len = 0x01; - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to erase fw @0x%02x: ", addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_ERASE_PAGE_INVALID_ADDR) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to erase @%04x: invalid page", - addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_ERASE_PAGE_NONZERO_START) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to erase @%04x: byte 0x00 is not 0xff", - addr); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_bootloader_nordic_write_firmware (LuDevice *device, GBytes *fw, GError **error) -{ - const LuDeviceBootloaderRequest *payload; - guint16 addr; - g_autoptr(GPtrArray) reqs = NULL; - - /* erase firmware pages up to the bootloader */ - for (addr = lu_device_bootloader_get_addr_lo (device); - addr < lu_device_bootloader_get_addr_hi (device); - addr += lu_device_bootloader_get_blocksize (device)) { - if (!lu_device_bootloader_nordic_erase (device, addr, error)) - return FALSE; - } - - /* transfer payload */ - reqs = lu_device_bootloader_parse_requests (device, fw, error); - if (reqs == NULL) - return FALSE; - - for (guint i = 1; i < reqs->len; i++) { - gboolean res; - payload = g_ptr_array_index (reqs, i); - - if (payload->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_SIGNATURE) { - res = lu_device_bootloader_nordic_write_signature(device, - payload->addr, - payload->len, - payload->data, - error); - } else { - res = lu_device_bootloader_nordic_write (device, - payload->addr, - payload->len, - payload->data, - error); - } - - if (!res) - return FALSE; - fu_device_set_progress_full (FU_DEVICE (device), i * 32, reqs->len * 32); - } - - /* send the first managed packet last, excluding the reset vector */ - payload = g_ptr_array_index (reqs, 0); - if (!lu_device_bootloader_nordic_write (device, - payload->addr + 1, - payload->len - 1, - payload->data + 1, - error)) - return FALSE; - - if (!lu_device_bootloader_nordic_write (device, - 0x0000, - 0x01, - payload->data, - error)) - return FALSE; - - /* mark as complete */ - fu_device_set_progress_full (FU_DEVICE (device), reqs->len * 32, reqs->len * 32); - - /* success! */ - return TRUE; -} - -static void -lu_device_bootloader_nordic_class_init (LuDeviceBootloaderNordicClass *klass) -{ - LuDeviceClass *klass_device = LU_DEVICE_CLASS (klass); - LuDeviceBootloaderClass *klass_device_bootloader = LU_DEVICE_BOOTLOADER_CLASS (klass); - klass_device->write_firmware = lu_device_bootloader_nordic_write_firmware; - klass_device_bootloader->probe = lu_device_bootloader_nordic_probe; -} - -static void -lu_device_bootloader_nordic_init (LuDeviceBootloaderNordic *device) -{ -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-bootloader-nordic.h fwupd-1.2.10/plugins/unifying/lu-device-bootloader-nordic.h --- fwupd-1.0.9/plugins/unifying/lu-device-bootloader-nordic.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-bootloader-nordic.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_DEVICE_BOOTLOADER_NORDIC_H -#define __LU_DEVICE_BOOTLOADER_NORDIC_H - -#include "lu-device-bootloader.h" - -G_BEGIN_DECLS - -#define LU_TYPE_DEVICE_BOOTLOADER_NORDIC (lu_device_bootloader_nordic_get_type ()) -G_DECLARE_FINAL_TYPE (LuDeviceBootloaderNordic, lu_device_bootloader_nordic, LU, DEVICE_BOOTLOADER_NORDIC, LuDeviceBootloader) - -G_END_DECLS - -#endif /* __LU_DEVICE_BOOTLOADER_NORDIC_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-bootloader-texas.c fwupd-1.2.10/plugins/unifying/lu-device-bootloader-texas.c --- fwupd-1.0.9/plugins/unifying/lu-device-bootloader-texas.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-bootloader-texas.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,221 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "lu-common.h" -#include "lu-device-bootloader-texas.h" - -struct _LuDeviceBootloaderTexas -{ - LuDeviceBootloader parent_instance; -}; - -G_DEFINE_TYPE (LuDeviceBootloaderTexas, lu_device_bootloader_texas, LU_TYPE_DEVICE_BOOTLOADER) - -static gboolean -lu_device_bootloader_texas_erase_all (LuDevice *device, GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM; - req->len = 0x01; /* magic number */ - req->data[0] = 0x00; /* magic number */ - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to erase all pages: "); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_bootloader_texas_compute_and_test_crc (LuDevice *device, GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM; - req->len = 0x01; /* magic number */ - req->data[0] = 0x03; /* magic number */ - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to compute and test CRC: "); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "CRC is incorrect"); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_bootloader_texas_flash_ram_buffer (LuDevice *device, guint16 addr, GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM; - req->addr = addr; - req->len = 0x01; /* magic number */ - req->data[0] = 0x01; /* magic number */ - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to flash ram buffer @%04x: ", addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to flash ram buffer @%04x: invalid flash page", - addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to flash ram buffer @%04x: invalid App JMP vector", - addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to flash ram buffer @%04x: page flashed before page 0", - addr); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_bootloader_texas_clear_ram_buffer (LuDevice *device, guint16 addr, GError **error) -{ - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - req->cmd = LU_DEVICE_BOOTLOADER_CMD_FLASH_RAM; - req->addr = addr; - req->len = 0x01; /* magic number */ - req->data[0] = 0x02; /* magic number */ - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, "failed to clear ram buffer @%04x: ", addr); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_bootloader_texas_write_firmware (LuDevice *device, GBytes *fw, GError **error) -{ - const LuDeviceBootloaderRequest *payload; - g_autoptr(GPtrArray) reqs = NULL; - g_autoptr(LuDeviceBootloaderRequest) req = lu_device_bootloader_request_new (); - - /* transfer payload */ - reqs = lu_device_bootloader_parse_requests (device, fw, error); - if (reqs == NULL) - return FALSE; - - /* erase all flash pages */ - if (!lu_device_bootloader_texas_erase_all (device, error)) - return FALSE; - - /* set existing RAM buffer to 0xff's */ - if (!lu_device_bootloader_texas_clear_ram_buffer (device, 0x0000, error)) - return FALSE; - - /* transfer payload */ - for (guint i = 0; i < reqs->len; i++) { - payload = g_ptr_array_index (reqs, i); - - /* check size */ - if (payload->len != 16) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "payload size invalid @%04x: got 0x%02x", - payload->addr, payload->len); - return FALSE; - } - - /* build packet */ - req->cmd = payload->cmd; - - /* signature addresses do not need to fit inside 128 bytes */ - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_SIGNATURE) - req->addr = payload->addr; - else - req->addr = payload->addr % 0x80; - - req->len = payload->len; - memcpy (req->data, payload->data, payload->len); - if (!lu_device_bootloader_request (device, req, error)) { - g_prefix_error (error, - "failed to write ram bufer @0x%02x: ", - req->addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write ram buffer @%04x: invalid location", - req->addr); - return FALSE; - } - if (req->cmd == LU_DEVICE_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to write ram buffer @%04x: invalid size 0x%02x", - req->addr, req->len); - return FALSE; - } - - /* flush RAM buffer to EEPROM */ - if ((payload->addr + 0x10) % 0x80 == 0 && - req->cmd != LU_DEVICE_BOOTLOADER_CMD_WRITE_SIGNATURE) { - guint16 addr_start = payload->addr - (7 * 0x10); - g_debug ("addr flush @ 0x%04x for 0x%04x", - payload->addr, addr_start); - if (!lu_device_bootloader_texas_flash_ram_buffer (device, - addr_start, - error)) { - g_prefix_error (error, - "failed to flash ram buffer @0x%04x: ", - addr_start); - return FALSE; - } - } - - /* update progress */ - fu_device_set_progress_full (FU_DEVICE (device), i * 32, reqs->len * 32); - } - - /* check CRC */ - if (!lu_device_bootloader_texas_compute_and_test_crc (device, error)) - return FALSE; - - /* mark as complete */ - fu_device_set_progress_full (FU_DEVICE (device), reqs->len * 32, reqs->len * 32); - - /* success! */ - return TRUE; -} - -static void -lu_device_bootloader_texas_class_init (LuDeviceBootloaderTexasClass *klass) -{ - LuDeviceClass *klass_device = LU_DEVICE_CLASS (klass); - klass_device->write_firmware = lu_device_bootloader_texas_write_firmware; -} - -static void -lu_device_bootloader_texas_init (LuDeviceBootloaderTexas *device) -{ - fu_device_set_version (FU_DEVICE (device), "RQR24.00_B0000"); -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-bootloader-texas.h fwupd-1.2.10/plugins/unifying/lu-device-bootloader-texas.h --- fwupd-1.0.9/plugins/unifying/lu-device-bootloader-texas.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-bootloader-texas.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_DEVICE_BOOTLOADER_TEXAS_H -#define __LU_DEVICE_BOOTLOADER_TEXAS_H - -#include "lu-device-bootloader.h" - -G_BEGIN_DECLS - -#define LU_TYPE_DEVICE_BOOTLOADER_TEXAS (lu_device_bootloader_texas_get_type ()) -G_DECLARE_FINAL_TYPE (LuDeviceBootloaderTexas, lu_device_bootloader_texas, LU, DEVICE_BOOTLOADER_TEXAS, LuDeviceBootloader) - -G_END_DECLS - -#endif /* __LU_DEVICE_BOOTLOADER_TEXAS_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-device.c fwupd-1.2.10/plugins/unifying/lu-device.c --- fwupd-1.0.9/plugins/unifying/lu-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1130 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "lu-common.h" -#include "lu-device-bootloader-nordic.h" -#include "lu-device-bootloader-texas.h" -#include "lu-device.h" -#include "lu-device-runtime.h" -#include "lu-hidpp.h" - -typedef struct -{ - LuDeviceKind kind; - GUdevDevice *udev_device; - gint udev_device_fd; - GUsbDevice *usb_device; - FuDeviceLocker *usb_device_locker; - gchar *version_hw; - LuDeviceFlags flags; - guint8 hidpp_id; - guint8 battery_level; - gdouble hidpp_version; - GPtrArray *feature_index; -} LuDevicePrivate; - -typedef struct { - guint8 idx; - guint16 feature; -} LuDeviceHidppMap; - -G_DEFINE_TYPE_WITH_PRIVATE (LuDevice, lu_device, FU_TYPE_DEVICE) - -enum { - PROP_0, - PROP_KIND, - PROP_HIDPP_ID, - PROP_FLAGS, - PROP_UDEV_DEVICE, - PROP_USB_DEVICE, - PROP_LAST -}; - -#define GET_PRIVATE(o) (lu_device_get_instance_private (o)) - -LuDeviceKind -lu_device_kind_from_string (const gchar *kind) -{ - if (g_strcmp0 (kind, "runtime") == 0) - return LU_DEVICE_KIND_RUNTIME; - if (g_strcmp0 (kind, "bootloader-nordic") == 0) - return LU_DEVICE_KIND_BOOTLOADER_NORDIC; - if (g_strcmp0 (kind, "bootloader-texas") == 0) - return LU_DEVICE_KIND_BOOTLOADER_TEXAS; - if (g_strcmp0 (kind, "peripheral") == 0) - return LU_DEVICE_KIND_PERIPHERAL; - return LU_DEVICE_KIND_UNKNOWN; -} - -const gchar * -lu_device_kind_to_string (LuDeviceKind kind) -{ - if (kind == LU_DEVICE_KIND_RUNTIME) - return "runtime"; - if (kind == LU_DEVICE_KIND_BOOTLOADER_NORDIC) - return "bootloader-nordic"; - if (kind == LU_DEVICE_KIND_BOOTLOADER_TEXAS) - return "bootloader-texas"; - if (kind == LU_DEVICE_KIND_PERIPHERAL) - return "peripheral"; - return NULL; -} - -static const gchar * -lu_hidpp_feature_to_string (guint16 feature) -{ - if (feature == HIDPP_FEATURE_ROOT) - return "Root"; - if (feature == HIDPP_FEATURE_I_FIRMWARE_INFO) - return "IFirmwareInfo"; - if (feature == HIDPP_FEATURE_GET_DEVICE_NAME_TYPE) - return "GetDevicenameType"; - if (feature == HIDPP_FEATURE_BATTERY_LEVEL_STATUS) - return "BatteryLevelStatus"; - if (feature == HIDPP_FEATURE_DFU_CONTROL) - return "DfuControl"; - if (feature == HIDPP_FEATURE_DFU_CONTROL_SIGNED) - return "DfuControlSigned"; - if (feature == HIDPP_FEATURE_DFU) - return "Dfu"; - return NULL; -} - -static gchar * -lu_device_flags_to_string (LuDeviceFlags flags) -{ - GString *str = g_string_new (NULL); - if (flags & LU_DEVICE_FLAG_REQUIRES_SIGNED_FIRMWARE) - g_string_append (str, "signed-firmware,"); - if (flags & LU_DEVICE_FLAG_REQUIRES_RESET) - g_string_append (str, "requires-reset,"); - if (flags & LU_DEVICE_FLAG_ACTIVE) - g_string_append (str, "active,"); - if (flags & LU_DEVICE_FLAG_IS_OPEN) - g_string_append (str, "is-open,"); - if (flags & LU_DEVICE_FLAG_REQUIRES_ATTACH) - g_string_append (str, "requires-attach,"); - if (flags & LU_DEVICE_FLAG_REQUIRES_DETACH) - g_string_append (str, "requires-detach,"); - if (flags & LU_DEVICE_FLAG_DETACH_WILL_REPLUG) - g_string_append (str, "detach-will-replug,"); - if (str->len == 0) { - g_string_append (str, "none"); - } else { - g_string_truncate (str, str->len - 1); - } - return g_string_free (str, FALSE); -} - -static void -lu_device_to_string (FuDevice *device, GString *str) -{ - LuDevice *self = LU_DEVICE (device); - LuDevicePrivate *priv = GET_PRIVATE (self); - g_autofree gchar *flags_str = NULL; - - g_string_append_printf (str, " Type:\t\t\t%s\n", lu_device_kind_to_string (priv->kind)); - flags_str = lu_device_flags_to_string (priv->flags); - g_string_append_printf (str, " Flags:\t\t%s\n", flags_str); - g_string_append_printf (str, " HidppVersion:\t\t%.2f\n", priv->hidpp_version); - if (priv->hidpp_id != HIDPP_DEVICE_ID_UNSET) - g_string_append_printf (str, " HidppId:\t\t0x%02x\n", (guint) priv->hidpp_id); - if (priv->udev_device_fd > 0) - g_string_append_printf (str, " UdevDevice:\t\t%i\n", priv->udev_device_fd); - if (priv->usb_device != NULL) - g_string_append_printf (str, " UsbDevice:\t\t%p\n", priv->usb_device); - if (priv->version_hw != NULL) - g_string_append_printf (str, " VersionHardware:\t%s\n", priv->version_hw); - if (priv->battery_level != 0) - g_string_append_printf (str, " Battery-level:\t\t%u\n", priv->battery_level); - for (guint i = 0; i < priv->feature_index->len; i++) { - LuDeviceHidppMap *map = g_ptr_array_index (priv->feature_index, i); - g_string_append_printf (str, " Feature%02x:\t\t%s [0x%04x]\n", - map->idx, - lu_hidpp_feature_to_string (map->feature), - map->feature); - } - - /* fixme: superclass? */ - if (LU_IS_DEVICE_BOOTLOADER (device)) { - g_string_append_printf (str, " FlashAddrHigh:\t0x%04x\n", - lu_device_bootloader_get_addr_hi (self)); - g_string_append_printf (str, " FlashAddrLow:\t0x%04x\n", - lu_device_bootloader_get_addr_lo (self)); - g_string_append_printf (str, " FlashBlockSize:\t0x%04x\n", - lu_device_bootloader_get_blocksize (self)); - } -} - -guint8 -lu_device_hidpp_feature_get_idx (LuDevice *device, guint16 feature) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - for (guint i = 0; i < priv->feature_index->len; i++) { - LuDeviceHidppMap *map = g_ptr_array_index (priv->feature_index, i); - if (map->feature == feature) - return map->idx; - } - return 0x00; -} - -guint16 -lu_device_hidpp_feature_find_by_idx (LuDevice *device, guint8 idx) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - for (guint i = 0; i < priv->feature_index->len; i++) { - LuDeviceHidppMap *map = g_ptr_array_index (priv->feature_index, i); - if (map->idx == idx) - return map->feature; - } - return 0x0000; -} - -static void -lu_device_hidpp_dump (LuDevice *device, const gchar *title, const guint8 *data, gsize len) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - g_autofree gchar *title_prefixed = NULL; - if (priv->usb_device != NULL) - title_prefixed = g_strdup_printf ("[USB] %s", title); - else if (priv->udev_device != NULL) - title_prefixed = g_strdup_printf ("[HID] %s", title); - else - title_prefixed = g_strdup_printf ("[EMU] %s", title); - lu_dump_raw (title_prefixed, data, len); -} - -static const gchar * -lu_device_hidpp20_function_to_string (guint16 feature, guint8 function_id) -{ - if (feature == HIDPP_FEATURE_ROOT) { - if (function_id == 0x00) - return "getFeature"; - if (function_id == 0x01) - return "ping"; - return NULL; - } - if (feature == HIDPP_FEATURE_I_FIRMWARE_INFO) { - if (function_id == 0x00) - return "getCount"; - if (function_id == 0x01) - return "getInfo"; - return NULL; - } - if (feature == HIDPP_FEATURE_BATTERY_LEVEL_STATUS) { - if (function_id == 0x00) - return "GetBatteryLevelStatus"; - return NULL; - } - if (feature == HIDPP_FEATURE_DFU_CONTROL) { - if (function_id == 0x00) - return "getDfuControl"; - if (function_id == 0x01) - return "setDfuControl"; - return NULL; - } - if (feature == HIDPP_FEATURE_DFU_CONTROL_SIGNED) { - if (function_id == 0x00) - return "getDfuStatus"; - if (function_id == 0x01) - return "startDfu"; - return NULL; - } - if (feature == HIDPP_FEATURE_DFU) { - if (function_id == 0x00) - return "dfuCmdData0"; - if (function_id == 0x01) - return "dfuCmdData1"; - if (function_id == 0x02) - return "dfuCmdData2"; - if (function_id == 0x03) - return "dfuCmdData3"; - if (function_id == 0x04) - return "dfuStart"; - if (function_id == 0x05) - return "restart"; - return NULL; - } - return NULL; -} - -static gchar * -lu_device_hidpp_msg_to_string (LuDevice *device, LuHidppMsg *msg) -{ - GString *str = g_string_new (NULL); - LuDevicePrivate *priv = GET_PRIVATE (device); - const gchar *tmp; - const gchar *kind_str = lu_device_kind_to_string (priv->kind); - g_autoptr(GError) error = NULL; - g_autoptr(GString) flags_str = g_string_new (NULL); - - g_return_val_if_fail (msg != NULL, NULL); - - g_string_append_printf (str, "device-kind: %s\n", kind_str); - if (msg->flags == LU_HIDPP_MSG_FLAG_NONE) { - g_string_append (flags_str, "none"); - } else { - if (msg->flags & LU_HIDPP_MSG_FLAG_LONGER_TIMEOUT) - g_string_append (flags_str, "longer-timeout,"); - if (msg->flags & LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID) - g_string_append (flags_str, "ignore-sub-id,"); - if (msg->flags & LU_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) - g_string_append (flags_str, "ignore-fnct-id,"); - if (msg->flags & LU_HIDPP_MSG_FLAG_IGNORE_SWID) - g_string_append (flags_str, "ignore-swid,"); - if (str->len > 0) - g_string_truncate (str, str->len - 1); - } - g_string_append_printf (str, "flags: %02x [%s]\n", - msg->flags, - flags_str->str); - g_string_append_printf (str, "report-id: %02x [%s]\n", - msg->report_id, - lu_hidpp_msg_rpt_id_to_string (msg)); - tmp = lu_hidpp_msg_dev_id_to_string (msg); - g_string_append_printf (str, "device-id: %02x [%s]\n", - msg->device_id, tmp ); - if (priv->hidpp_version >= 2.f) { - guint16 feature = lu_device_hidpp_feature_find_by_idx (device, msg->sub_id); - guint8 sw_id = msg->function_id & 0x0f; - guint8 function_id = (msg->function_id & 0xf0) >> 4; - g_string_append_printf (str, "feature: %04x [%s]\n", - feature, - lu_hidpp_feature_to_string (feature)); - g_string_append_printf (str, "function-id: %02x [%s]\n", - function_id, - lu_device_hidpp20_function_to_string (feature, function_id)); - g_string_append_printf (str, "sw-id: %02x [%s]\n", - sw_id, - sw_id == LU_HIDPP_MSG_SW_ID ? "fwupd" : "???"); - } else { - g_string_append_printf (str, "sub-id: %02x [%s]\n", - msg->sub_id, - lu_hidpp_msg_sub_id_to_string (msg)); - g_string_append_printf (str, "function-id: %02x [%s]\n", - msg->function_id, - lu_hidpp_msg_fcn_id_to_string (msg)); - } - if (!lu_hidpp_msg_is_error (msg, &error)) { - g_string_append_printf (str, "error: %s\n", - error->message); - } - return g_string_free (str, FALSE); -} - -gboolean -lu_device_hidpp_send (LuDevice *device, - LuHidppMsg *msg, - guint timeout, - GError **error) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - gsize len = lu_hidpp_msg_get_payload_length (msg); - - /* only for HID++2.0 */ - if (lu_device_get_hidpp_version (device) >= 2.f) - msg->function_id |= LU_HIDPP_MSG_SW_ID; - - lu_device_hidpp_dump (device, "host->device", (guint8 *) msg, len); - - /* detailed debugging */ - if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { - g_autofree gchar *str = lu_device_hidpp_msg_to_string (device, msg); - g_print ("%s", str); - } - - /* USB */ - if (priv->usb_device != NULL) { - gsize actual_length = 0; - if (!g_usb_device_control_transfer (priv->usb_device, - G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - LU_REQUEST_SET_REPORT, - 0x0210, 0x0002, - (guint8 *) msg, len, - &actual_length, - timeout, - NULL, - error)) { - g_prefix_error (error, "failed to send data: "); - return FALSE; - } - if (actual_length != len) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to send data: " - "wrote %" G_GSIZE_FORMAT " of %" G_GSIZE_FORMAT, - actual_length, len); - return FALSE; - } - - /* HID */ - } else if (priv->udev_device != NULL) { - if (!lu_nonblock_write (priv->udev_device_fd, - (guint8 *) msg, len, error)) { - g_prefix_error (error, "failed to send: "); - return FALSE; - } - } - - /* success */ - return TRUE; -} - -gboolean -lu_device_hidpp_receive (LuDevice *device, - LuHidppMsg *msg, - guint timeout, - GError **error) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - gsize read_size = 0; - - /* USB */ - if (priv->usb_device != NULL) { - if (!g_usb_device_interrupt_transfer (priv->usb_device, - LU_DEVICE_EP3, - (guint8 *) msg, - sizeof(LuHidppMsg), - &read_size, - timeout, - NULL, - error)) { - g_prefix_error (error, "failed to get data: "); - return FALSE; - } - - /* HID */ - } else if (priv->udev_device != NULL) { - if (!lu_nonblock_read (priv->udev_device_fd, - (guint8 *) msg, - sizeof(LuHidppMsg), - &read_size, - timeout, - error)) { - g_prefix_error (error, "failed to receive: "); - return FALSE; - } - } - - /* check long enough, but allow returning oversize packets */ - lu_device_hidpp_dump (device, "device->host", (guint8 *) msg, read_size); - if (read_size < lu_hidpp_msg_get_payload_length (msg)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "message length too small, " - "got %" G_GSIZE_FORMAT " expected %" G_GSIZE_FORMAT, - read_size, lu_hidpp_msg_get_payload_length (msg)); - return FALSE; - } - - /* detailed debugging */ - if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { - g_autofree gchar *str = lu_device_hidpp_msg_to_string (device, msg); - g_print ("%s", str); - } - - /* success */ - return TRUE; -} - -gboolean -lu_device_hidpp_transfer (LuDevice *device, LuHidppMsg *msg, GError **error) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - guint timeout = LU_DEVICE_TIMEOUT_MS; - g_autoptr(LuHidppMsg) msg_tmp = lu_hidpp_msg_new (); - - /* increase timeout for some operations */ - if (msg->flags & LU_HIDPP_MSG_FLAG_LONGER_TIMEOUT) - timeout *= 10; - - /* send request */ - if (!lu_device_hidpp_send (device, msg, timeout, error)) - return FALSE; - - /* keep trying to receive until we get a valid reply */ - while (1) { - if (!lu_device_hidpp_receive (device, msg_tmp, timeout, error)) - return FALSE; - - /* we don't know how to handle this report packet */ - if (lu_hidpp_msg_get_payload_length (msg_tmp) == 0x0) { - g_debug ("HID++1.0 report 0x%02x has unknown length, ignoring", - msg_tmp->report_id); - continue; - } - - if (!lu_hidpp_msg_is_error (msg_tmp, error)) - return FALSE; - - /* is valid reply */ - if (lu_hidpp_msg_is_reply (msg, msg_tmp)) - break; - - /* to ensure compatibility when an HID++ 2.0 device is - * connected to an HID++ 1.0 receiver, any feature index - * corresponding to an HID++ 1.0 sub-identifier which could be - * sent by the receiver, must be assigned to a dummy feature */ - if (lu_device_get_hidpp_version (device) >= 2.f) { - if (lu_hidpp_msg_is_hidpp10_compat (msg_tmp)) { - g_debug ("ignoring HID++1.0 reply"); - continue; - } - - /* not us */ - if ((msg->flags & LU_HIDPP_MSG_FLAG_IGNORE_SWID) == 0) { - if (!lu_hidpp_msg_verify_swid (msg_tmp)) { - g_debug ("ignoring reply with SwId 0x%02i, expected 0x%02i", - msg_tmp->function_id & 0x0f, - LU_HIDPP_MSG_SW_ID); - continue; - } - } - } - - g_debug ("ignoring message"); - - }; - - /* if the HID++ ID is unset, grab it from the reply */ - if (priv->hidpp_id == HIDPP_DEVICE_ID_UNSET) { - priv->hidpp_id = msg_tmp->device_id; - g_debug ("HID++ ID now %02x", priv->hidpp_id); - } - - /* copy over data */ - lu_hidpp_msg_copy (msg, msg_tmp); - return TRUE; -} - -gboolean -lu_device_hidpp_feature_search (LuDevice *device, guint16 feature, GError **error) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - LuDeviceHidppMap *map; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* find the idx for the feature */ - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = priv->hidpp_id; - msg->sub_id = 0x00; /* rootIndex */ - msg->function_id = 0x00 << 4; /* getFeature */ - msg->data[0] = feature >> 8; - msg->data[1] = feature; - msg->data[2] = 0x00; - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, - "failed to get idx for feature %s [0x%04x]: ", - lu_hidpp_feature_to_string (feature), feature); - return FALSE; - } - - /* zero index */ - if (msg->data[0] == 0x00) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "feature %s [0x%04x] not found", - lu_hidpp_feature_to_string (feature), feature); - return FALSE; - } - - /* add to map */ - map = g_new0 (LuDeviceHidppMap, 1); - map->idx = msg->data[0]; - map->feature = feature; - g_ptr_array_add (priv->feature_index, map); - g_debug ("added feature %s [0x%04x] as idx %02x", - lu_hidpp_feature_to_string (feature), feature, map->idx); - return TRUE; -} - -LuDeviceKind -lu_device_get_kind (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->kind; -} - -guint8 -lu_device_get_hidpp_id (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->hidpp_id; -} - -void -lu_device_set_hidpp_id (LuDevice *device, guint8 hidpp_id) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - priv->hidpp_id = hidpp_id; -} - -guint8 -lu_device_get_battery_level (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->battery_level; -} - -void -lu_device_set_battery_level (LuDevice *device, guint8 percentage) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - priv->battery_level = percentage; -} - -gdouble -lu_device_get_hidpp_version (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->hidpp_version; -} - -void -lu_device_set_hidpp_version (LuDevice *device, gdouble hidpp_version) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - priv->hidpp_version = hidpp_version; -} - -const gchar * -lu_device_get_version_hw (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->version_hw; -} - -void -lu_device_set_version_hw (LuDevice *device, const gchar *version_hw) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - g_free (priv->version_hw); - priv->version_hw = g_strdup (version_hw); -} - -gboolean -lu_device_has_flag (LuDevice *device, LuDeviceFlags flag) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return (priv->flags & flag) > 0; -} - -void -lu_device_add_flag (LuDevice *device, LuDeviceFlags flag) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - priv->flags |= flag; - g_object_notify (G_OBJECT (device), "flags"); -} - -void -lu_device_remove_flag (LuDevice *device, LuDeviceFlags flag) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - priv->flags &= ~flag; - g_object_notify (G_OBJECT (device), "flags"); -} - -LuDeviceFlags -lu_device_get_flags (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->flags; -} - -GUdevDevice * -lu_device_get_udev_device (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->udev_device; -} - -GUsbDevice * -lu_device_get_usb_device (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - return priv->usb_device; -} - -gboolean -lu_device_probe (LuDevice *device, GError **error) -{ - LuDeviceClass *klass = LU_DEVICE_GET_CLASS (device); - LuDevicePrivate *priv = GET_PRIVATE (device); - - /* clear the feature map (leaving only the root) */ - g_ptr_array_set_size (priv->feature_index, 0); - - /* probe the hardware */ - if (klass->probe != NULL) - return klass->probe (device, error); - return TRUE; -} - -gboolean -lu_device_open (LuDevice *device, GError **error) -{ - LuDeviceClass *klass = LU_DEVICE_GET_CLASS (device); - LuDevicePrivate *priv = GET_PRIVATE (device); - g_autofree gchar *device_str = NULL; - - g_return_val_if_fail (LU_IS_DEVICE (device), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* already done */ - if (lu_device_has_flag (device, LU_DEVICE_FLAG_IS_OPEN)) - return TRUE; - - /* set default vendor */ - fu_device_set_vendor (FU_DEVICE (device), "Logitech"); - - /* USB */ - if (priv->usb_device != NULL) { - guint8 num_interfaces = 0x01; - g_autofree gchar *devid = NULL; - - /* open device */ - if (priv->usb_device_locker == NULL) { - g_autoptr(FuDeviceLocker) locker = NULL; - g_debug ("opening unifying device using USB"); - locker = fu_device_locker_new (priv->usb_device, error); - if (locker == NULL) - return FALSE; - if (priv->kind == LU_DEVICE_KIND_RUNTIME) - num_interfaces = 0x03; - for (guint i = 0; i < num_interfaces; i++) { - g_debug ("claiming interface 0x%02x", i); - if (!g_usb_device_claim_interface (priv->usb_device, i, - G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, - error)) { - g_prefix_error (error, "Failed to claim 0x%02x: ", i); - return FALSE; - } - } - priv->usb_device_locker = g_steal_pointer (&locker); - } - - /* generate GUID */ - devid = g_strdup_printf ("USB\\VID_%04X&PID_%04X", - g_usb_device_get_vid (priv->usb_device), - g_usb_device_get_pid (priv->usb_device)); - fu_device_add_guid (FU_DEVICE (device), devid); - - /* HID */ - } else if (priv->udev_device != NULL) { - const gchar *devpath = g_udev_device_get_device_file (priv->udev_device); - g_debug ("opening unifying device using %s", devpath); - priv->udev_device_fd = lu_nonblock_open (devpath, error); - if (priv->udev_device_fd < 0) - return FALSE; - } - - /* subclassed */ - if (klass->open != NULL) { - if (!klass->open (device, error)) { - lu_device_close (device, NULL); - return FALSE; - } - } - lu_device_add_flag (device, LU_DEVICE_FLAG_IS_OPEN); - - /* subclassed */ - if (!lu_device_probe (device, error)) { - lu_device_close (device, NULL); - return FALSE; - } - - /* add known root for HID++2.0 */ - if (lu_device_get_hidpp_version (device) >= 2.f) { - LuDeviceHidppMap *map = g_new0 (LuDeviceHidppMap, 1); - map->idx = 0x00; - map->feature = HIDPP_FEATURE_ROOT; - g_ptr_array_add (priv->feature_index, map); - } - - /* show the device */ - device_str = fu_device_to_string (FU_DEVICE (device)); - g_debug ("%s", device_str); - - /* success */ - return TRUE; -} - -gboolean -lu_device_poll (LuDevice *device, GError **error) -{ - LuDeviceClass *klass = LU_DEVICE_GET_CLASS (device); - if (klass->poll != NULL) { - if (!klass->poll (device, error)) - return FALSE; - } - return TRUE; -} - -/** - * lu_device_close: - * @device: A #LuDevice - * @error: A #GError, or %NULL - * - * Closes the device. - * - * Returns: %TRUE for success - **/ -gboolean -lu_device_close (LuDevice *device, GError **error) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - LuDeviceClass *klass = LU_DEVICE_GET_CLASS (device); - - g_return_val_if_fail (LU_IS_DEVICE (device), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* not open */ - if (!lu_device_has_flag (device, LU_DEVICE_FLAG_IS_OPEN)) - return TRUE; - - /* subclassed */ - g_debug ("closing device"); - if (klass->close != NULL) { - if (!klass->close (device, error)) - return FALSE; - } - - /* USB */ - if (priv->usb_device_locker != NULL) { - guint8 num_interfaces = 0x01; - if (priv->kind == LU_DEVICE_KIND_RUNTIME) - num_interfaces = 0x03; - for (guint i = 0; i < num_interfaces; i++) { - g_autoptr(GError) error_local = NULL; - g_debug ("releasing interface 0x%02x", i); - if (!g_usb_device_release_interface (priv->usb_device, i, - G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, - &error_local)) { - if (!g_error_matches (error_local, - G_USB_DEVICE_ERROR, - G_USB_DEVICE_ERROR_INTERNAL)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "Failed to release 0x%02x: %s", - i, error_local->message); - return FALSE; - } - } - } - g_clear_object (&priv->usb_device_locker); - } - g_clear_object (&priv->usb_device); - - /* HID */ - if (priv->udev_device != NULL && priv->udev_device_fd > 0) { - if (!g_close (priv->udev_device_fd, error)) - return FALSE; - priv->udev_device_fd = 0; - } - - /* success */ - lu_device_remove_flag (device, LU_DEVICE_FLAG_IS_OPEN); - return TRUE; -} -static gboolean -lu_device_detach (FuDevice *device, GError **error) -{ - LuDevice *self = LU_DEVICE (device); - LuDeviceClass *klass = LU_DEVICE_GET_CLASS (device); - - g_return_val_if_fail (LU_IS_DEVICE (device), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* subclassed */ - g_debug ("detaching device"); - if (klass->detach != NULL) - return klass->detach (self, error); - - /* nothing to do */ - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "device detach is not supported"); - return FALSE; -} - -static gboolean -lu_device_attach (FuDevice *device, GError **error) -{ - LuDevice *self = LU_DEVICE (device); - LuDeviceClass *klass = LU_DEVICE_GET_CLASS (device); - - g_return_val_if_fail (LU_IS_DEVICE (device), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* check kind */ - if (lu_device_get_kind (self) == LU_DEVICE_KIND_RUNTIME) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "device is not in bootloader state"); - return FALSE; - } - - /* subclassed */ - if (klass->attach != NULL) - return klass->attach (self, error); - - return TRUE; -} - -static gboolean -lu_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) -{ - LuDevice *self = LU_DEVICE (device); - LuDeviceClass *klass = LU_DEVICE_GET_CLASS (self); - - g_return_val_if_fail (LU_IS_DEVICE (device), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* corrupt */ - if (g_bytes_get_size (fw) < 0x4000) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "firmware is too small"); - return FALSE; - } - - /* call device-specific method */ - if (klass->write_firmware == NULL) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "not supported in %s", - lu_device_kind_to_string (lu_device_get_kind (self))); - return FALSE; - } - - /* call either nordic or texas vfunc */ - return klass->write_firmware (self, fw, error); -} - -#ifndef HAVE_GUDEV_232 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevClient, g_object_unref) -#pragma clang diagnostic pop -#endif - -static GUdevDevice * -lu_device_find_udev_device (GUsbDevice *usb_device) -{ - g_autoptr(GUdevClient) gudev_client = g_udev_client_new (NULL); - g_autoptr(GList) devices = NULL; - - devices = g_udev_client_query_by_subsystem (gudev_client, "usb"); - for (GList *l = devices; l != NULL; l = l->next) { - guint busnum; - guint devnum; - g_autoptr(GUdevDevice) udev_device = G_UDEV_DEVICE (l->data); - g_autoptr(GUdevDevice) udev_parent = g_udev_device_get_parent (udev_device); - - busnum = g_udev_device_get_sysfs_attr_as_int (udev_parent, "busnum"); - if (busnum != g_usb_device_get_bus (usb_device)) - continue; - devnum = g_udev_device_get_sysfs_attr_as_int (udev_parent, "devnum"); - if (devnum != g_usb_device_get_address (usb_device)) - continue; - - return g_object_ref (udev_parent); - } - return NULL; -} - -static void -lu_device_update_platform_id (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - if (priv->usb_device != NULL && priv->udev_device == NULL) { - g_autoptr(GUdevDevice) udev_device = NULL; - udev_device = lu_device_find_udev_device (priv->usb_device); - if (udev_device != NULL) { - const gchar *tmp = g_udev_device_get_sysfs_path (udev_device); - fu_device_set_platform_id (FU_DEVICE (device), tmp); - } - } -} - -static void -lu_device_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - LuDevice *device = LU_DEVICE (object); - LuDevicePrivate *priv = GET_PRIVATE (device); - switch (prop_id) { - case PROP_KIND: - g_value_set_uint (value, priv->kind); - break; - case PROP_HIDPP_ID: - g_value_set_uint (value, priv->hidpp_id); - break; - case PROP_FLAGS: - g_value_set_uint64 (value, priv->flags); - break; - case PROP_UDEV_DEVICE: - g_value_set_object (value, priv->udev_device); - break; - case PROP_USB_DEVICE: - g_value_set_object (value, priv->usb_device); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -lu_device_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - LuDevice *device = LU_DEVICE (object); - LuDevicePrivate *priv = GET_PRIVATE (device); - switch (prop_id) { - case PROP_KIND: - priv->kind = g_value_get_uint (value); - break; - case PROP_HIDPP_ID: - priv->hidpp_id = g_value_get_uint (value); - break; - case PROP_FLAGS: - priv->flags = g_value_get_uint64 (value); - break; - case PROP_UDEV_DEVICE: - priv->udev_device = g_value_dup_object (value); - break; - case PROP_USB_DEVICE: - priv->usb_device = g_value_dup_object (value); - lu_device_update_platform_id (device); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -lu_device_finalize (GObject *object) -{ - LuDevice *device = LU_DEVICE (object); - LuDevicePrivate *priv = GET_PRIVATE (device); - - if (priv->usb_device != NULL) - g_object_unref (priv->usb_device); - if (priv->usb_device_locker != NULL) - g_object_unref (priv->usb_device_locker); - if (priv->udev_device != NULL) - g_object_unref (priv->udev_device); - if (priv->udev_device_fd > 0) - g_close (priv->udev_device_fd, NULL); - g_ptr_array_unref (priv->feature_index); - g_free (priv->version_hw); - - G_OBJECT_CLASS (lu_device_parent_class)->finalize (object); -} - -static void -lu_device_init (LuDevice *device) -{ - LuDevicePrivate *priv = GET_PRIVATE (device); - priv->hidpp_id = HIDPP_DEVICE_ID_UNSET; - priv->feature_index = g_ptr_array_new_with_free_func (g_free); - fu_device_set_vendor_id (FU_DEVICE (device), "USB:0x046D"); -} - -static void -lu_device_class_init (LuDeviceClass *klass) -{ - GParamSpec *pspec; - GObjectClass *object_class = G_OBJECT_CLASS (klass); - FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); - - object_class->finalize = lu_device_finalize; - object_class->get_property = lu_device_get_property; - object_class->set_property = lu_device_set_property; - klass_device->to_string = lu_device_to_string; - klass_device->write_firmware = lu_device_write_firmware; - klass_device->attach = lu_device_attach; - klass_device->detach = lu_device_detach; - - pspec = g_param_spec_uint ("kind", NULL, NULL, - LU_DEVICE_KIND_UNKNOWN, - LU_DEVICE_KIND_LAST, - LU_DEVICE_KIND_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_KIND, pspec); - - pspec = g_param_spec_uint ("hidpp-id", NULL, NULL, - HIDPP_DEVICE_ID_WIRED, - HIDPP_DEVICE_ID_RECEIVER, - HIDPP_DEVICE_ID_UNSET, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_HIDPP_ID, pspec); - - pspec = g_param_spec_uint64 ("flags", NULL, NULL, - LU_DEVICE_FLAG_NONE, - 0xffff, - LU_DEVICE_FLAG_NONE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_FLAGS, pspec); - - pspec = g_param_spec_object ("udev-device", NULL, NULL, - G_UDEV_TYPE_DEVICE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_UDEV_DEVICE, pspec); - - pspec = g_param_spec_object ("usb-device", NULL, NULL, - G_USB_TYPE_DEVICE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_USB_DEVICE, pspec); -} - -LuDevice * -lu_device_fake_new (LuDeviceKind kind) -{ - LuDevice *device = NULL; - switch (kind) { - case LU_DEVICE_KIND_BOOTLOADER_NORDIC: - device = g_object_new (LU_TYPE_DEVICE_BOOTLOADER_NORDIC, - "kind", kind, - NULL); - break; - case LU_DEVICE_KIND_BOOTLOADER_TEXAS: - device = g_object_new (LU_TYPE_DEVICE_BOOTLOADER_TEXAS, - "kind", kind, - NULL); - break; - case LU_DEVICE_KIND_RUNTIME: - device = g_object_new (LU_TYPE_DEVICE_RUNTIME, - "kind", kind, - NULL); - break; - default: - break; - } - return device; -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-device.h fwupd-1.2.10/plugins/unifying/lu-device.h --- fwupd-1.0.9/plugins/unifying/lu-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_DEVICE_H -#define __LU_DEVICE_H - -#include -#include - -#include "fu-plugin.h" - -#include "lu-hidpp-msg.h" - -G_BEGIN_DECLS - -#define LU_TYPE_DEVICE (lu_device_get_type ()) -G_DECLARE_DERIVABLE_TYPE (LuDevice, lu_device, LU, DEVICE, FuDevice) - -struct _LuDeviceClass -{ - FuDeviceClass parent_class; - gboolean (*open) (LuDevice *device, - GError **error); - gboolean (*close) (LuDevice *device, - GError **error); - gboolean (*probe) (LuDevice *device, - GError **error); - gboolean (*poll) (LuDevice *device, - GError **error); - gboolean (*attach) (LuDevice *device, - GError **error); - gboolean (*detach) (LuDevice *device, - GError **error); - gboolean (*write_firmware) (LuDevice *device, - GBytes *fw, - GError **error); -}; - -#define LU_DEVICE_VID 0x046d - -#define LU_DEVICE_PID_RUNTIME 0xc52b -#define LU_DEVICE_PID_BOOTLOADER_NORDIC 0xaaaa -#define LU_DEVICE_PID_BOOTLOADER_NORDIC_PICO 0xaaae -#define LU_DEVICE_PID_BOOTLOADER_TEXAS 0xaaac -#define LU_DEVICE_PID_BOOTLOADER_TEXAS_PICO 0xaaad - -#define LU_DEVICE_EP1 0x81 -#define LU_DEVICE_EP3 0x83 -/* Signed firmware are very long to verify on the device */ -#define LU_DEVICE_TIMEOUT_MS 20000 - -typedef enum { - LU_DEVICE_KIND_UNKNOWN, - LU_DEVICE_KIND_RUNTIME, - LU_DEVICE_KIND_BOOTLOADER_NORDIC, - LU_DEVICE_KIND_BOOTLOADER_TEXAS, - LU_DEVICE_KIND_PERIPHERAL, - /*< private >*/ - LU_DEVICE_KIND_LAST -} LuDeviceKind; - -typedef enum { - LU_DEVICE_FLAG_NONE, - LU_DEVICE_FLAG_ACTIVE = 1 << 0, - LU_DEVICE_FLAG_IS_OPEN = 1 << 1, - LU_DEVICE_FLAG_REQUIRES_SIGNED_FIRMWARE = 1 << 3, - LU_DEVICE_FLAG_REQUIRES_RESET = 1 << 4, - LU_DEVICE_FLAG_REQUIRES_ATTACH = 1 << 5, - LU_DEVICE_FLAG_REQUIRES_DETACH = 1 << 6, - LU_DEVICE_FLAG_ATTACH_WILL_REPLUG = 1 << 7, - LU_DEVICE_FLAG_DETACH_WILL_REPLUG = 1 << 8, - /*< private >*/ - LU_DEVICE_FLAG_LAST -} LuDeviceFlags; - -LuDeviceKind lu_device_kind_from_string (const gchar *kind); -const gchar *lu_device_kind_to_string (LuDeviceKind kind); - -LuDeviceKind lu_device_get_kind (LuDevice *device); -guint8 lu_device_get_hidpp_id (LuDevice *device); -void lu_device_set_hidpp_id (LuDevice *device, - guint8 hidpp_id); -guint8 lu_device_get_battery_level (LuDevice *device); -void lu_device_set_battery_level (LuDevice *device, - guint8 percentage); -gdouble lu_device_get_hidpp_version (LuDevice *device); -void lu_device_set_hidpp_version (LuDevice *device, - gdouble hidpp_version); -gboolean lu_device_has_flag (LuDevice *device, - LuDeviceFlags flag); -void lu_device_add_flag (LuDevice *device, - LuDeviceFlags flag); -void lu_device_remove_flag (LuDevice *device, - LuDeviceFlags flag); -LuDeviceFlags lu_device_get_flags (LuDevice *device); -const gchar *lu_device_get_version_hw (LuDevice *device); -void lu_device_set_version_hw (LuDevice *device, - const gchar *version_hw); -GUdevDevice *lu_device_get_udev_device (LuDevice *device); -GUsbDevice *lu_device_get_usb_device (LuDevice *device); - -LuDevice *lu_device_fake_new (LuDeviceKind kind); -gboolean lu_device_open (LuDevice *device, - GError **error); -gboolean lu_device_close (LuDevice *device, - GError **error); -gboolean lu_device_probe (LuDevice *device, - GError **error); -gboolean lu_device_poll (LuDevice *device, - GError **error); -gboolean lu_device_hidpp_send (LuDevice *device, - LuHidppMsg *msg, - guint timeout, - GError **error); -gboolean lu_device_hidpp_receive (LuDevice *device, - LuHidppMsg *msg, - guint timeout, - GError **error); -gboolean lu_device_hidpp_transfer (LuDevice *device, - LuHidppMsg *msg, - GError **error); -gboolean lu_device_hidpp_feature_search (LuDevice *device, - guint16 feature, - GError **error); -guint8 lu_device_hidpp_feature_get_idx (LuDevice *device, - guint16 feature); -guint16 lu_device_hidpp_feature_find_by_idx (LuDevice *device, - guint8 idx); - -G_END_DECLS - -#endif /* __LU_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-peripheral.c fwupd-1.2.10/plugins/unifying/lu-device-peripheral.c --- fwupd-1.0.9/plugins/unifying/lu-device-peripheral.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-peripheral.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,785 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "lu-common.h" -#include "lu-device-peripheral.h" -#include "lu-hidpp.h" - -struct _LuDevicePeripheral -{ - LuDevice parent_instance; - guint8 cached_fw_entity; -}; - -G_DEFINE_TYPE (LuDevicePeripheral, lu_device_peripheral, LU_TYPE_DEVICE) - -typedef enum { - LU_DEVICE_PERIPHERAL_KIND_KEYBOARD, - LU_DEVICE_PERIPHERAL_KIND_REMOTE_CONTROL, - LU_DEVICE_PERIPHERAL_KIND_NUMPAD, - LU_DEVICE_PERIPHERAL_KIND_MOUSE, - LU_DEVICE_PERIPHERAL_KIND_TOUCHPAD, - LU_DEVICE_PERIPHERAL_KIND_TRACKBALL, - LU_DEVICE_PERIPHERAL_KIND_PRESENTER, - LU_DEVICE_PERIPHERAL_KIND_RECEIVER, - LU_DEVICE_PERIPHERAL_KIND_LAST -} LuDevicePeripheralKind; - -static const gchar * -lu_device_peripheral_get_icon (LuDevicePeripheralKind kind) -{ - if (kind == LU_DEVICE_PERIPHERAL_KIND_KEYBOARD) - return "input-keyboard"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_REMOTE_CONTROL) - return "pda"; // ish - if (kind == LU_DEVICE_PERIPHERAL_KIND_NUMPAD) - return "input-dialpad"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_MOUSE) - return "input-mouse"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_TOUCHPAD) - return "input-touchpad"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_TRACKBALL) - return "input-mouse"; // ish - if (kind == LU_DEVICE_PERIPHERAL_KIND_PRESENTER) - return "pda"; // ish - if (kind == LU_DEVICE_PERIPHERAL_KIND_RECEIVER) - return "preferences-desktop-keyboard"; - return NULL; -} - -static const gchar * -lu_device_peripheral_get_summary (LuDevicePeripheralKind kind) -{ - if (kind == LU_DEVICE_PERIPHERAL_KIND_KEYBOARD) - return "Unifying Keyboard"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_REMOTE_CONTROL) - return "Unifying Remote Control"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_NUMPAD) - return "Unifying Number Pad"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_MOUSE) - return "Unifying Mouse"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_TOUCHPAD) - return "Unifying Touchpad"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_TRACKBALL) - return "Unifying Trackball"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_PRESENTER) - return "Unifying Presenter"; - if (kind == LU_DEVICE_PERIPHERAL_KIND_RECEIVER) - return "Unifying Receiver"; - return NULL; -} - -static gboolean -lu_device_peripheral_fetch_firmware_info (LuDevice *device, GError **error) -{ - LuDevicePeripheral *self = LU_DEVICE_PERIPHERAL (device); - guint8 idx; - guint8 entity_count; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* get the feature index */ - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_I_FIRMWARE_INFO); - if (idx == 0x00) - return TRUE; - - /* get the entity count */ - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x00 << 4; /* getCount */ - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to get firmware count: "); - return FALSE; - } - entity_count = msg->data[0]; - g_debug ("firmware entity count is %u", entity_count); - - /* get firmware, bootloader, hardware versions */ - for (guint8 i = 0; i < entity_count; i++) { - guint16 build; - g_autofree gchar *version = NULL; - g_autofree gchar *name = NULL; - - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x01 << 4; /* getInfo */ - msg->data[0] = i; - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to get firmware info: "); - return FALSE; - } - if (msg->data[1] == 0x00 && - msg->data[2] == 0x00 && - msg->data[3] == 0x00 && - msg->data[4] == 0x00 && - msg->data[4] == 0x00 && - msg->data[5] == 0x00 && - msg->data[6] == 0x00 && - msg->data[7] == 0x00) { - g_debug ("no version set for entity %u", i); - continue; - } - name = g_strdup_printf ("%c%c%c", - msg->data[1], - msg->data[2], - msg->data[3]); - build = ((guint16) msg->data[6]) << 8 | msg->data[7]; - version = lu_format_version (name, - msg->data[4], - msg->data[5], - build); - g_debug ("firmware entity 0x%02x version is %s", i, version); - if (msg->data[0] == 0) { - fu_device_set_version (device, version); - self->cached_fw_entity = i; - } else if (msg->data[0] == 1) { - fu_device_set_version_bootloader (FU_DEVICE (device), version); - } else if (msg->data[0] == 2) { - lu_device_set_version_hw (device, version); - } - } - - /* not an error, the device just doesn't support this */ - return TRUE; -} - -static gboolean -lu_device_peripheral_fetch_battery_level (LuDevice *device, GError **error) -{ - /* try using HID++2.0 */ - if (lu_device_get_hidpp_version (device) >= 2.f) { - guint8 idx; - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_BATTERY_LEVEL_STATUS); - if (idx != 0x00) { - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x00; /* GetBatteryLevelStatus */ - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to get battery info: "); - return FALSE; - } - if (msg->data[0] != 0x00) - lu_device_set_battery_level (device, msg->data[0]); - return TRUE; - } - } - - /* try HID++1.0 battery mileage */ - if (lu_device_get_hidpp_version (device) == 1.f) { - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = HIDPP_SUBID_GET_REGISTER; - msg->function_id = HIDPP_REGISTER_BATTERY_MILEAGE; - if (lu_device_hidpp_transfer (device, msg, NULL)) { - if (msg->data[0] != 0x00) - lu_device_set_battery_level (device, msg->data[0]); - return TRUE; - } - - /* try HID++1.0 battery status instead */ - msg->function_id = HIDPP_REGISTER_BATTERY_STATUS; - if (lu_device_hidpp_transfer (device, msg, NULL)) { - switch (msg->data[0]) { - case 1: /* 0 - 10 */ - lu_device_set_battery_level (device, 5); - break; - case 3: /* 11 - 30 */ - lu_device_set_battery_level (device, 20); - break; - case 5: /* 31 - 80 */ - lu_device_set_battery_level (device, 55); - break; - case 7: /* 81 - 100 */ - lu_device_set_battery_level (device, 90); - break; - default: - g_warning ("unknown battery percentage: 0x%02x", - msg->data[0]); - break; - } - return TRUE; - } - } - - /* not an error, the device just doesn't support any of the methods */ - return TRUE; -} - -static gboolean -lu_device_peripheral_ping (LuDevice *device, GError **error) -{ - gdouble version; - g_autoptr(GError) error_local = NULL; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* handle failure */ - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = 0x00; /* rootIndex */ - msg->function_id = 0x01 << 4; /* ping */ - msg->data[0] = 0x00; - msg->data[1] = 0x00; - msg->data[2] = 0xaa; /* user-selected value */ - if (!lu_device_hidpp_transfer (device, msg, &error_local)) { - if (g_error_matches (error_local, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED)) { - lu_device_set_hidpp_version (device, 1.f); - return TRUE; - } - if (g_error_matches (error_local, - G_IO_ERROR, - G_IO_ERROR_HOST_UNREACHABLE)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_HOST_UNREACHABLE, - "device %s is unreachable: %s", - fu_device_get_name (device), - error_local->message); - lu_device_remove_flag (device, LU_DEVICE_FLAG_ACTIVE); - return FALSE; - } - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to ping %s: %s", - fu_device_get_name (FU_DEVICE (device)), - error_local->message); - return FALSE; - } - - /* format version in BCD format */ - version = (gdouble) msg->data[0] + ((gdouble) msg->data[1]) / 100.f; - lu_device_set_hidpp_version (device, version); - - /* success */ - return TRUE; -} - -static gboolean -lu_device_peripheral_probe (LuDevice *device, GError **error) -{ - guint8 idx; - const guint16 map_features[] = { - HIDPP_FEATURE_GET_DEVICE_NAME_TYPE, - HIDPP_FEATURE_I_FIRMWARE_INFO, - HIDPP_FEATURE_BATTERY_LEVEL_STATUS, - HIDPP_FEATURE_DFU_CONTROL, - HIDPP_FEATURE_DFU_CONTROL_SIGNED, - HIDPP_FEATURE_DFU, - HIDPP_FEATURE_ROOT }; - - /* ping device to get HID++ version */ - if (!lu_device_peripheral_ping (device, error)) - return FALSE; - - /* map some *optional* HID++2.0 features we might use */ - for (guint i = 0; map_features[i] != HIDPP_FEATURE_ROOT; i++) { - g_autoptr(GError) error_local = NULL; - if (!lu_device_hidpp_feature_search (device, - map_features[i], - &error_local)) { - g_debug ("%s", error_local->message); - if (g_error_matches (error_local, - G_IO_ERROR, - G_IO_ERROR_TIMED_OUT)) { - /* timed out, so not trying any more */ - break; - } - } - } - - /* get the firmware information */ - if (!lu_device_peripheral_fetch_firmware_info (device, error)) - return FALSE; - - /* get the battery level */ - if (!lu_device_peripheral_fetch_battery_level (device, error)) - return FALSE; - - /* try using HID++2.0 */ - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE); - if (idx != 0x00) { - const gchar *tmp; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x02 << 4; /* getDeviceType */ - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to get device type: "); - return FALSE; - } - - /* add nice-to-have data */ - tmp = lu_device_peripheral_get_summary (msg->data[0]); - if (tmp != NULL) - fu_device_set_summary (FU_DEVICE (device), tmp); - tmp = lu_device_peripheral_get_icon (msg->data[0]); - if (tmp != NULL) - fu_device_add_icon (FU_DEVICE (device), tmp); - } - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_DFU_CONTROL); - if (idx != 0x00) { - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); - lu_device_add_flag (device, LU_DEVICE_FLAG_REQUIRES_DETACH); - } - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_DFU_CONTROL_SIGNED); - if (idx != 0x00) { - /* check the feature is available */ - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x00 << 4; /* getDfuStatus */ - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to get DFU status: "); - return FALSE; - } - if ((msg->data[2] & 0x01) > 0) { - g_warning ("DFU mode not available"); - } else { - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); - lu_device_add_flag (device, LU_DEVICE_FLAG_REQUIRES_DETACH); - lu_device_add_flag (device, LU_DEVICE_FLAG_REQUIRES_SIGNED_FIRMWARE); - } - } - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_DFU); - if (idx != 0x00) { - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); - lu_device_add_flag (device, LU_DEVICE_FLAG_REQUIRES_ATTACH); - if (fu_device_get_version (device) == NULL) { - g_debug ("repairing device in bootloader mode"); - fu_device_set_version (FU_DEVICE (device), "MPK00.00_B0000"); - } - } - - /* this device is active right now */ - lu_device_add_flag (device, LU_DEVICE_FLAG_ACTIVE); - return TRUE; -} - -static gboolean -lu_device_peripheral_detach (LuDevice *device, GError **error) -{ - guint8 idx; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* this requires user action */ - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_DFU_CONTROL); - if (idx != 0x00) { - msg->report_id = HIDPP_REPORT_ID_LONG; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x01 << 4; /* setDfuControl */ - msg->data[0] = 0x01; /* enterDfu */ - msg->data[1] = 0x00; /* dfuControlParam */ - msg->data[2] = 0x00; /* unused */ - msg->data[3] = 0x00; /* unused */ - msg->data[4] = 'D'; - msg->data[5] = 'F'; - msg->data[6] = 'U'; - msg->flags = LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID | - LU_HIDPP_MSG_FLAG_LONGER_TIMEOUT; - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to put device into DFU mode: "); - return FALSE; - } - lu_device_add_flag (device, LU_DEVICE_FLAG_REQUIRES_RESET); - return TRUE; - } - - /* this can reboot all by itself */ - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_DFU_CONTROL_SIGNED); - if (idx != 0x00) { - msg->report_id = HIDPP_REPORT_ID_LONG; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x01 << 4; /* setDfuControl */ - msg->data[0] = 0x01; /* startDfu */ - msg->data[1] = 0x00; /* dfuControlParam */ - msg->data[2] = 0x00; /* unused */ - msg->data[3] = 0x00; /* unused */ - msg->data[4] = 'D'; - msg->data[5] = 'F'; - msg->data[6] = 'U'; - msg->flags = LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID; - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to put device into DFU mode: "); - return FALSE; - } - - /* reprobe */ - return lu_device_probe (device, error); - } - - /* we don't know how */ - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "no method to detach"); - return FALSE; -} - -static gboolean -lu_device_peripheral_check_status (guint8 status, GError **error) -{ - switch (status & 0x7f) { - case 0x00: - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "invalid status value 0x%02x", - status); - break; - case 0x01: /* packet success */ - case 0x02: /* DFU success */ - case 0x05: /* DFU success: entity restart required */ - case 0x06: /* DFU success: system restart required */ - /* success */ - return TRUE; - break; - case 0x03: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_PENDING, - "wait for event (command in progress)"); - break; - case 0x04: - case 0x10: /* unknown */ - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "generic error"); - break; - case 0x11: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "bad voltage (power too low?)"); - break; - case 0x12: - case 0x14: /* bad magic string */ - case 0x21: /* bad firmware */ - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "unsupported firmware"); - break; - case 0x13: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "unsupported encryption mode"); - break; - case 0x15: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "erase failure"); - break; - case 0x16: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "DFU not started"); - break; - case 0x17: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "bad sequence number"); - break; - case 0x18: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "unsupported command"); - break; - case 0x19: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "command in progress"); - break; - case 0x1a: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "address out of range"); - break; - case 0x1b: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "unaligned address"); - break; - case 0x1c: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "bad size"); - break; - case 0x1d: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "missing program data"); - break; - case 0x1e: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "missing check data"); - break; - case 0x1f: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "program failed to write"); - break; - case 0x20: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "program failed to verify"); - break; - case 0x22: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "firmware check failure"); - break; - case 0x23: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "blocked command (restart required)"); - break; - default: - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "unhandled status value 0x%02x", - status); - break; - } - return FALSE; -} - -static gboolean -lu_device_peripheral_write_firmware_pkt (LuDevice *device, - guint8 idx, - guint8 cmd, - const guint8 *data, - GError **error) -{ - guint32 packet_cnt; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - g_autoptr(GError) error_local = NULL; - - /* send firmware data */ - msg->report_id = HIDPP_REPORT_ID_LONG; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = cmd << 4; /* dfuStart or dfuCmdDataX */ - memcpy (msg->data, data, 16); - if (!lu_device_hidpp_transfer (device, msg, &error_local)) { - g_prefix_error (error, "failed to supply program data: "); - return FALSE; - } - - /* check error */ - packet_cnt = fu_common_read_uint32 (msg->data, G_BIG_ENDIAN); - g_debug ("packet_cnt=0x%04x", packet_cnt); - if (lu_device_peripheral_check_status (msg->data[4], &error_local)) - return TRUE; - - /* fatal error */ - if (!g_error_matches (error_local, - G_IO_ERROR, - G_IO_ERROR_PENDING)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - error_local->message); - return FALSE; - } - - /* wait for the HID++ notification */ - g_debug ("ignoring: %s", error_local->message); - for (guint retry = 0; retry < 10; retry++) { - g_autoptr(LuHidppMsg) msg2 = lu_hidpp_msg_new (); - msg2->flags = LU_HIDPP_MSG_FLAG_IGNORE_FNCT_ID; - if (!lu_device_hidpp_receive (device, msg2, 15000, error)) - return FALSE; - if (lu_hidpp_msg_is_reply (msg, msg2)) { - g_autoptr(GError) error2 = NULL; - if (!lu_device_peripheral_check_status (msg2->data[4], &error2)) { - g_debug ("got %s, waiting a bit longer", error2->message); - continue; - } - return TRUE; - } else { - g_debug ("got wrong packet, continue to wait..."); - } - } - - /* nothing in the queue */ - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to get event after timeout"); - return FALSE; -} - -static gboolean -lu_device_peripheral_write_firmware (LuDevice *device, GBytes *fw, GError **error) -{ - gsize sz = 0; - const guint8 *data; - guint8 cmd = 0x04; - guint8 idx; - - /* if we're in bootloader mode, we should be able to get this feature */ - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_DFU); - if (idx == 0x00) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "no DFU feature available"); - return FALSE; - } - - /* flash hardware */ - data = g_bytes_get_data (fw, &sz); - for (gsize i = 0; i < sz / 16; i++) { - - /* send packet and wait for reply */ - g_debug ("send data at addr=0x%04x", (guint) i * 16); - if (!lu_device_peripheral_write_firmware_pkt (device, - idx, - cmd, - data + (i * 16), - error)) { - g_prefix_error (error, - "failed to write @0x%04x: ", - (guint) i * 16); - return FALSE; - } - - /* use sliding window */ - cmd = (cmd + 1) % 4; - - /* update progress-bar */ - fu_device_set_progress_full (FU_DEVICE (device), i * 16, sz); - } - - return TRUE; -} - -static gboolean -lu_device_peripheral_attach (LuDevice *device, GError **error) -{ - LuDevicePeripheral *self = LU_DEVICE_PERIPHERAL (device); - guint8 idx; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* if we're in bootloader mode, we should be able to get this feature */ - idx = lu_device_hidpp_feature_get_idx (device, HIDPP_FEATURE_DFU); - if (idx == 0x00) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "no DFU feature available"); - return FALSE; - } - - /* reboot back into firmware mode */ - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = idx; - msg->function_id = 0x05 << 4; /* restart */ - msg->data[0] = self->cached_fw_entity; /* fwEntity */ - msg->flags = LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID | - LU_HIDPP_MSG_FLAG_IGNORE_SWID | // inferred? - LU_HIDPP_MSG_FLAG_LONGER_TIMEOUT; - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to restart device: "); - return FALSE; - } - - /* reprobe */ - if (!lu_device_probe (device, error)) - return FALSE; - - /* success */ - return TRUE; -} - -static gboolean -lu_device_peripheral_poll (LuDevice *device, GError **error) -{ - const guint timeout = 1; /* ms */ - g_autoptr(GError) error_local = NULL; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* flush pending data */ - if (!lu_device_hidpp_receive (device, msg, timeout, &error_local)) { - if (!g_error_matches (error_local, - G_IO_ERROR, - G_IO_ERROR_TIMED_OUT)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to get pending read: %s", - error_local->message); - return FALSE; - } - } - - /* just ping */ - if (lu_device_has_flag (device, LU_DEVICE_FLAG_ACTIVE)) - return lu_device_peripheral_ping (device, error); - - /* probe, which also involves a ping first */ - return lu_device_probe (device, error); -} - -static void -lu_device_peripheral_finalize (GObject *object) -{ - G_OBJECT_CLASS (lu_device_peripheral_parent_class)->finalize (object); -} - -static void -lu_device_peripheral_class_init (LuDevicePeripheralClass *klass) -{ - LuDeviceClass *klass_device = LU_DEVICE_CLASS (klass); - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = lu_device_peripheral_finalize; - klass_device->probe = lu_device_peripheral_probe; - klass_device->poll = lu_device_peripheral_poll; - klass_device->write_firmware = lu_device_peripheral_write_firmware; - klass_device->attach = lu_device_peripheral_attach; - klass_device->detach = lu_device_peripheral_detach; -} - -static void -lu_device_peripheral_init (LuDevicePeripheral *self) -{ - fu_device_add_parent_guid (FU_DEVICE (self), "USB\\VID_046D&PID_C52B"); -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-peripheral.h fwupd-1.2.10/plugins/unifying/lu-device-peripheral.h --- fwupd-1.0.9/plugins/unifying/lu-device-peripheral.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-peripheral.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_DEVICE_PERIPHERAL_H -#define __LU_DEVICE_PERIPHERAL_H - -#include "lu-device.h" - -G_BEGIN_DECLS - -#define LU_TYPE_DEVICE_PERIPHERAL (lu_device_peripheral_get_type ()) -G_DECLARE_FINAL_TYPE (LuDevicePeripheral, lu_device_peripheral, LU, DEVICE_PERIPHERAL, LuDevice) - -G_END_DECLS - -#endif /* __LU_DEVICE_PERIPHERAL_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-runtime.c fwupd-1.2.10/plugins/unifying/lu-device-runtime.c --- fwupd-1.0.9/plugins/unifying/lu-device-runtime.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-runtime.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,243 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "lu-common.h" -#include "lu-device-runtime.h" -#include "lu-hidpp.h" - -struct _LuDeviceRuntime -{ - LuDevice parent_instance; -}; - -G_DEFINE_TYPE (LuDeviceRuntime, lu_device_runtime, LU_TYPE_DEVICE) - -#ifndef HAVE_GUDEV_232 -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) -#endif - -static gboolean -lu_device_runtime_enable_notifications (LuDevice *device, GError **error) -{ - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = HIDPP_SUBID_SET_REGISTER; - msg->function_id = HIDPP_REGISTER_HIDPP_NOTIFICATIONS; - msg->data[0] = 0x00; - msg->data[1] = 0x05; /* Wireless + SoftwarePresent */ - msg->data[2] = 0x00; - return lu_device_hidpp_transfer (device, msg, error); -} - -static gboolean -lu_device_runtime_open (LuDevice *device, GError **error) -{ - GUdevDevice *udev_device = lu_device_get_udev_device (device); - GUsbDevice *usb_device = lu_device_get_usb_device (device); - guint16 release = 0xffff; - guint8 config[10]; - guint8 version_bl_major = 0; - g_autofree gchar *devid1 = NULL; - g_autofree gchar *version_bl = NULL; - g_autofree gchar *version_fw = NULL; - - /* add a generic GUID */ - devid1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", - (guint) LU_DEVICE_VID, - (guint) LU_DEVICE_PID_RUNTIME); - fu_device_add_guid (FU_DEVICE (device), devid1); - - /* generate bootloadder-specific GUID */ - if (usb_device != NULL) { - release = g_usb_device_get_release (usb_device); - } else if (udev_device != NULL) { - g_autoptr(GUdevDevice) udev_parent = NULL; - udev_parent = g_udev_device_get_parent_with_subsystem (udev_device, - "usb", "usb_device"); - if (udev_parent != NULL) { - const gchar *release_str; - release_str = g_udev_device_get_property (udev_parent, "ID_REVISION"); - if (release_str != NULL) - release = g_ascii_strtoull (release_str, NULL, 16); - } - } - if (release != 0xffff) { - g_autofree gchar *devid2 = NULL; - switch (release &= 0xff00) { - case 0x1200: - /* Nordic */ - devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", - (guint) LU_DEVICE_VID, - (guint) LU_DEVICE_PID_BOOTLOADER_NORDIC); - fu_device_add_guid (FU_DEVICE (device), devid2); - version_bl_major = 0x01; - break; - case 0x2400: - /* Texas */ - devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", - (guint) LU_DEVICE_VID, - (guint) LU_DEVICE_PID_BOOTLOADER_TEXAS); - fu_device_add_guid (FU_DEVICE (device), devid2); - version_bl_major = 0x03; - break; - default: - g_warning ("bootloader release %04x invalid", release); - break; - } - } - - /* read all 10 bytes of the version register */ - memset (config, 0x00, sizeof (config)); - for (guint i = 0x01; i < 0x05; i++) { - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* workaround a bug in the 12.01 firmware, which fails with - * INVALID_VALUE when reading MCU1_HW_VERSION */ - if (i == 0x03) - continue; - - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = HIDPP_SUBID_GET_REGISTER; - msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION; - msg->data[0] = i; - if (!lu_device_hidpp_transfer (device, msg, error)) { - g_prefix_error (error, "failed to read device config: "); - return FALSE; - } - memcpy (config + (i * 2), msg->data + 1, 2); - } - - /* get firmware version */ - version_fw = lu_format_version ("RQR", - config[2], - config[3], - (guint16) config[4] << 8 | - config[5]); - fu_device_set_version (FU_DEVICE (device), version_fw); - - /* get bootloader version */ - if (version_bl_major > 0) { - version_bl = lu_format_version ("BOT", - version_bl_major, - config[8], - config[9]); - fu_device_set_version_bootloader (FU_DEVICE (device), version_bl); - - /* is the dongle expecting signed firmware */ - if ((version_bl_major == 0x01 && config[8] >= 0x04) || - (version_bl_major == 0x03 && config[8] >= 0x02)) { - lu_device_add_flag (device, LU_DEVICE_FLAG_REQUIRES_SIGNED_FIRMWARE); - } - } - - /* enable HID++ notifications */ - if (!lu_device_runtime_enable_notifications (device, error)) { - g_prefix_error (error, "failed to enable notifications: "); - return FALSE; - } - - /* this only exists with the original HID++1.0 version */ - lu_device_set_hidpp_version (device, 1.f); - - /* we can flash this */ - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); - - /* only the bootloader can do the update */ - fu_device_set_name (FU_DEVICE (device), "Unifying Receiver"); - - return TRUE; -} - -static gboolean -lu_device_runtime_detach (LuDevice *device, GError **error) -{ - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - msg->report_id = HIDPP_REPORT_ID_SHORT; - msg->device_id = lu_device_get_hidpp_id (device); - msg->sub_id = HIDPP_SUBID_SET_REGISTER; - msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE; - msg->data[0] = 'I'; - msg->data[1] = 'C'; - msg->data[2] = 'P'; - msg->flags = LU_HIDPP_MSG_FLAG_LONGER_TIMEOUT; - if (!lu_device_hidpp_send (device, msg, LU_DEVICE_TIMEOUT_MS, error)) { - g_prefix_error (error, "failed to detach to bootloader: "); - return FALSE; - } - return TRUE; -} - -static gboolean -lu_device_runtime_poll (LuDevice *device, GError **error) -{ - const guint timeout = 1; /* ms */ - g_autoptr(GError) error_local = NULL; - g_autoptr(LuHidppMsg) msg = lu_hidpp_msg_new (); - - /* is there any pending data to read */ - if (!lu_device_hidpp_receive (device, msg, timeout, &error_local)) { - if (g_error_matches (error_local, - G_IO_ERROR, - G_IO_ERROR_TIMED_OUT)) { - return TRUE; - } - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to get pending read: %s", - error_local->message); - return FALSE; - } - - /* HID++1.0 error */ - if (!lu_hidpp_msg_is_error (msg, error)) - return FALSE; - - /* unifying receiver notification */ - if (msg->report_id == HIDPP_REPORT_ID_SHORT) { - switch (msg->sub_id) { - case HIDPP_SUBID_DEVICE_CONNECTION: - case HIDPP_SUBID_DEVICE_DISCONNECTION: - case HIDPP_SUBID_DEVICE_LOCKING_CHANGED: - g_debug ("device connection event, do something"); - break; - case HIDPP_SUBID_LINK_QUALITY: - g_debug ("ignoring link quality message"); - break; - case HIDPP_SUBID_ERROR_MSG: - g_debug ("ignoring link quality message"); - break; - default: - g_debug ("unknown SubID %02x", msg->sub_id); - break; - } - } - return TRUE; -} - -static void -lu_device_runtime_class_init (LuDeviceRuntimeClass *klass) -{ - LuDeviceClass *klass_device = LU_DEVICE_CLASS (klass); - klass_device->open = lu_device_runtime_open; - klass_device->poll = lu_device_runtime_poll; - klass_device->detach = lu_device_runtime_detach; -} - -static void -lu_device_runtime_init (LuDeviceRuntime *device) -{ - /* FIXME: we need something better */ - fu_device_add_icon (FU_DEVICE (device), "preferences-desktop-keyboard"); - fu_device_set_summary (FU_DEVICE (device), "A miniaturised USB wireless receiver"); -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-device-runtime.h fwupd-1.2.10/plugins/unifying/lu-device-runtime.h --- fwupd-1.0.9/plugins/unifying/lu-device-runtime.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-device-runtime.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_DEVICE_RUNTIME_H -#define __LU_DEVICE_RUNTIME_H - -#include "lu-device.h" - -G_BEGIN_DECLS - -#define LU_TYPE_DEVICE_RUNTIME (lu_device_runtime_get_type ()) -G_DECLARE_FINAL_TYPE (LuDeviceRuntime, lu_device_runtime, LU, DEVICE_RUNTIME, LuDevice) - -G_END_DECLS - -#endif /* __LU_DEVICE_RUNTIME_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-hidpp.h fwupd-1.2.10/plugins/unifying/lu-hidpp.h --- fwupd-1.0.9/plugins/unifying/lu-hidpp.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-hidpp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016-2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU__HIDPP_H -#define __LU__HIDPP_H - -G_BEGIN_DECLS - -#define LU_REQUEST_SET_REPORT 0x09 - -/* - * Based on the HID++ documentation provided by Nestor Lopez Casado at: - * https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing - */ -#define HIDPP_DEVICE_ID_WIRED 0x00 -#define HIDPP_DEVICE_ID_RECEIVER 0xFF -#define HIDPP_DEVICE_ID_UNSET 0xFE - -#define HIDPP_REPORT_NOTIFICATION 0x01 -#define HIDPP_REPORT_ID_SHORT 0x10 -#define HIDPP_REPORT_ID_LONG 0x11 -#define HIDPP_REPORT_ID_VERY_LONG 0x12 - -#define HIDPP_SUBID_VENDOR_SPECIFIC_KEYS 0x03 -#define HIDPP_SUBID_POWER_KEYS 0x04 -#define HIDPP_SUBID_ROLLER 0x05 -#define HIDPP_SUBID_MOUSE_EXTRA_BUTTONS 0x06 -#define HIDPP_SUBID_BATTERY_CHARGING_LEVEL 0x07 -#define HIDPP_SUBID_USER_INTERFACE_EVENT 0x08 -#define HIDPP_SUBID_F_LOCK_STATUS 0x09 -#define HIDPP_SUBID_CALCULATOR_RESULT 0x0A -#define HIDPP_SUBID_MENU_NAVIGATE 0x0B -#define HIDPP_SUBID_FN_KEY 0x0C -#define HIDPP_SUBID_BATTERY_MILEAGE 0x0D -#define HIDPP_SUBID_UART_RX 0x0E -#define HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE 0x17 -#define HIDPP_SUBID_DEVICE_DISCONNECTION 0x40 -#define HIDPP_SUBID_DEVICE_CONNECTION 0x41 -#define HIDPP_SUBID_DEVICE_DISCOVERY 0x42 -#define HIDPP_SUBID_PIN_CODE_REQUEST 0x43 -#define HIDPP_SUBID_RECEIVER_WORKING_MODE 0x44 -#define HIDPP_SUBID_ERROR_MESSAGE 0x45 -#define HIDPP_SUBID_RF_LINK_CHANGE 0x46 -#define HIDPP_SUBID_HCI 0x48 -#define HIDPP_SUBID_LINK_QUALITY 0x49 -#define HIDPP_SUBID_DEVICE_LOCKING_CHANGED 0x4a -#define HIDPP_SUBID_WIRELESS_DEVICE_CHANGE 0x4B -#define HIDPP_SUBID_ACL 0x51 -#define HIDPP_SUBID_VOIP_TELEPHONY_EVENT 0x5B -#define HIDPP_SUBID_LED 0x60 -#define HIDPP_SUBID_GESTURE_AND_AIR 0x65 -#define HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH 0x66 -#define HIDPP_SUBID_TRACEABILITY 0x78 -#define HIDPP_SUBID_SET_REGISTER 0x80 -#define HIDPP_SUBID_GET_REGISTER 0x81 -#define HIDPP_SUBID_SET_LONG_REGISTER 0x82 -#define HIDPP_SUBID_GET_LONG_REGISTER 0x83 -#define HIDPP_SUBID_SET_VERY_LONG_REGISTER 0x84 -#define HIDPP_SUBID_GET_VERY_LONG_REGISTER 0x85 -#define HIDPP_SUBID_ERROR_MSG 0x8F -#define HIDPP_SUBID_ERROR_MSG_20 0xFF - -#define HIDPP_ERR_SUCCESS 0x00 -#define HIDPP_ERR_INVALID_SUBID 0x01 -#define HIDPP_ERR_INVALID_ADDRESS 0x02 -#define HIDPP_ERR_INVALID_VALUE 0x03 -#define HIDPP_ERR_CONNECT_FAIL 0x04 -#define HIDPP_ERR_TOO_MANY_DEVICES 0x05 -#define HIDPP_ERR_ALREADY_EXISTS 0x06 -#define HIDPP_ERR_BUSY 0x07 -#define HIDPP_ERR_UNKNOWN_DEVICE 0x08 -#define HIDPP_ERR_RESOURCE_ERROR 0x09 -#define HIDPP_ERR_REQUEST_UNAVAILABLE 0x0A -#define HIDPP_ERR_INVALID_PARAM_VALUE 0x0B -#define HIDPP_ERR_WRONG_PIN_CODE 0x0C - -/* - * HID++1.0 registers - */ - -#define HIDPP_REGISTER_HIDPP_NOTIFICATIONS 0x00 -#define HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES 0x01 -#define HIDPP_REGISTER_BATTERY_STATUS 0x07 -#define HIDPP_REGISTER_BATTERY_MILEAGE 0x0D -#define HIDPP_REGISTER_PROFILE 0x0F -#define HIDPP_REGISTER_LED_STATUS 0x51 -#define HIDPP_REGISTER_LED_INTENSITY 0x54 -#define HIDPP_REGISTER_LED_COLOR 0x57 -#define HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS 0x61 -#define HIDPP_REGISTER_CURRENT_RESOLUTION 0x63 -#define HIDPP_REGISTER_USB_REFRESH_RATE 0x64 -#define HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT 0xA0 -#define HIDPP_REGISTER_HOT_CONTROL 0xA1 -#define HIDPP_REGISTER_READ_MEMORY 0xA2 -#define HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION 0xB2 -#define HIDPP_REGISTER_PAIRING_INFORMATION 0xB5 -#define HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE 0xF0 -#define HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION 0xF1 - -/* - * HID++2.0 error codes - */ -#define HIDPP_ERROR_CODE_NO_ERROR 0x00 -#define HIDPP_ERROR_CODE_UNKNOWN 0x01 -#define HIDPP_ERROR_CODE_INVALID_ARGUMENT 0x02 -#define HIDPP_ERROR_CODE_OUT_OF_RANGE 0x03 -#define HIDPP_ERROR_CODE_HW_ERROR 0x04 -#define HIDPP_ERROR_CODE_LOGITECH_INTERNAL 0x05 -#define HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX 0x06 -#define HIDPP_ERROR_CODE_INVALID_FUNCTION_ID 0x07 -#define HIDPP_ERROR_CODE_BUSY 0x08 -#define HIDPP_ERROR_CODE_UNSUPPORTED 0x09 - -/* - * HID++2.0 features - */ -#define HIDPP_FEATURE_ROOT 0x0000 -#define HIDPP_FEATURE_I_FEATURE_SET 0x0001 -#define HIDPP_FEATURE_I_FIRMWARE_INFO 0x0003 -#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE 0x0005 -#define HIDPP_FEATURE_DFU_CONTROL 0x00c1 -#define HIDPP_FEATURE_DFU_CONTROL_SIGNED 0x00c2 -#define HIDPP_FEATURE_DFU 0x00d0 -#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS 0x1000 -#define HIDPP_FEATURE_KBD_REPROGRAMMABLE_KEYS 0x1b00 -#define HIDPP_FEATURE_SPECIAL_KEYS_BUTTONS 0x1b04 -#define HIDPP_FEATURE_MOUSE_POINTER_BASIC 0x2200 -#define HIDPP_FEATURE_ADJUSTABLE_DPI 0x2201 -#define HIDPP_FEATURE_ADJUSTABLE_REPORT_RATE 0x8060 -#define HIDPP_FEATURE_COLOR_LED_EFFECTS 0x8070 -#define HIDPP_FEATURE_ONBOARD_PROFILES 0x8100 -#define HIDPP_FEATURE_MOUSE_BUTTON_SPY 0x8110 - -G_END_DECLS - -#endif /* __LU__HIDPP_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-hidpp-msg.c fwupd-1.2.10/plugins/unifying/lu-hidpp-msg.c --- fwupd-1.0.9/plugins/unifying/lu-hidpp-msg.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-hidpp-msg.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,400 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include -#include - -#include "lu-hidpp.h" -#include "lu-hidpp-msg.h" - -LuHidppMsg * -lu_hidpp_msg_new (void) -{ - return g_new0 (LuHidppMsg, 1); -} - -const gchar * -lu_hidpp_msg_dev_id_to_string (LuHidppMsg *msg) -{ - g_return_val_if_fail (msg != NULL, NULL); - if (msg->device_id == HIDPP_DEVICE_ID_WIRED) - return "wired"; - if (msg->device_id == HIDPP_DEVICE_ID_RECEIVER) - return "receiver"; - if (msg->device_id == HIDPP_DEVICE_ID_UNSET) - return "unset"; - return NULL; -} - -const gchar * -lu_hidpp_msg_rpt_id_to_string (LuHidppMsg *msg) -{ - g_return_val_if_fail (msg != NULL, NULL); - if (msg->report_id == HIDPP_REPORT_ID_SHORT) - return "short"; - if (msg->report_id == HIDPP_REPORT_ID_LONG) - return "long"; - if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) - return "very-long"; - return NULL; -} - -gsize -lu_hidpp_msg_get_payload_length (LuHidppMsg *msg) -{ - if (msg->report_id == HIDPP_REPORT_ID_SHORT) - return 0x07; - if (msg->report_id == HIDPP_REPORT_ID_LONG) - return 0x14; - if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG) - return 0x2f; - if (msg->report_id == HIDPP_REPORT_NOTIFICATION) - return 0x08; - return 0x0; -} - -const gchar * -lu_hidpp_msg_fcn_id_to_string (LuHidppMsg *msg) -{ - g_return_val_if_fail (msg != NULL, NULL); - switch (msg->sub_id) { - case HIDPP_SUBID_SET_REGISTER: - case HIDPP_SUBID_GET_REGISTER: - case HIDPP_SUBID_SET_LONG_REGISTER: - case HIDPP_SUBID_GET_LONG_REGISTER: - case HIDPP_SUBID_SET_VERY_LONG_REGISTER: - case HIDPP_SUBID_GET_VERY_LONG_REGISTER: - if (msg->function_id == HIDPP_REGISTER_HIDPP_NOTIFICATIONS) - return "hidpp-notifications"; - if (msg->function_id == HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES) - return "individual-features"; - if (msg->function_id == HIDPP_REGISTER_BATTERY_STATUS) - return "battery-status"; - if (msg->function_id == HIDPP_REGISTER_BATTERY_MILEAGE) - return "battery-mileage"; - if (msg->function_id == HIDPP_REGISTER_PROFILE) - return "profile"; - if (msg->function_id == HIDPP_REGISTER_LED_STATUS) - return "led-status"; - if (msg->function_id == HIDPP_REGISTER_LED_INTENSITY) - return "led-intensity"; - if (msg->function_id == HIDPP_REGISTER_LED_COLOR) - return "led-color"; - if (msg->function_id == HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS) - return "optical-sensor-settings"; - if (msg->function_id == HIDPP_REGISTER_CURRENT_RESOLUTION) - return "current-resolution"; - if (msg->function_id == HIDPP_REGISTER_USB_REFRESH_RATE) - return "usb-refresh-rate"; - if (msg->function_id == HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT) - return "generic-memory-management"; - if (msg->function_id == HIDPP_REGISTER_HOT_CONTROL) - return "hot-control"; - if (msg->function_id == HIDPP_REGISTER_READ_MEMORY) - return "read-memory"; - if (msg->function_id == HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION) - return "device-connection-disconnection"; - if (msg->function_id == HIDPP_REGISTER_PAIRING_INFORMATION) - return "pairing-information"; - if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE) - return "device-firmware-update-mode"; - if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION) - return "device-firmware-information"; - break; - default: - break; - } - return NULL; - -} - -const gchar * -lu_hidpp_msg_sub_id_to_string (LuHidppMsg *msg) -{ - g_return_val_if_fail (msg != NULL, NULL); - if (msg->sub_id == HIDPP_SUBID_VENDOR_SPECIFIC_KEYS) - return "vendor-specific-keys"; - if (msg->sub_id == HIDPP_SUBID_POWER_KEYS) - return "power-keys"; - if (msg->sub_id == HIDPP_SUBID_ROLLER) - return "roller"; - if (msg->sub_id == HIDPP_SUBID_MOUSE_EXTRA_BUTTONS) - return "mouse-extra-buttons"; - if (msg->sub_id == HIDPP_SUBID_BATTERY_CHARGING_LEVEL) - return "battery-charging-level"; - if (msg->sub_id == HIDPP_SUBID_USER_INTERFACE_EVENT) - return "user-interface-event"; - if (msg->sub_id == HIDPP_SUBID_F_LOCK_STATUS) - return "f-lock-status"; - if (msg->sub_id == HIDPP_SUBID_CALCULATOR_RESULT) - return "calculator-result"; - if (msg->sub_id == HIDPP_SUBID_MENU_NAVIGATE) - return "menu-navigate"; - if (msg->sub_id == HIDPP_SUBID_FN_KEY) - return "fn-key"; - if (msg->sub_id == HIDPP_SUBID_BATTERY_MILEAGE) - return "battery-mileage"; - if (msg->sub_id == HIDPP_SUBID_UART_RX) - return "uart-rx"; - if (msg->sub_id == HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE) - return "backlight-duration-update"; - if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCONNECTION) - return "device-disconnection"; - if (msg->sub_id == HIDPP_SUBID_DEVICE_CONNECTION) - return "device-connection"; - if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCOVERY) - return "device-discovery"; - if (msg->sub_id == HIDPP_SUBID_PIN_CODE_REQUEST) - return "pin-code-request"; - if (msg->sub_id == HIDPP_SUBID_RECEIVER_WORKING_MODE) - return "receiver-working-mode"; - if (msg->sub_id == HIDPP_SUBID_ERROR_MESSAGE) - return "error-message"; - if (msg->sub_id == HIDPP_SUBID_RF_LINK_CHANGE) - return "rf-link-change"; - if (msg->sub_id == HIDPP_SUBID_HCI) - return "hci"; - if (msg->sub_id == HIDPP_SUBID_LINK_QUALITY) - return "link-quality"; - if (msg->sub_id == HIDPP_SUBID_DEVICE_LOCKING_CHANGED) - return "device-locking-changed"; - if (msg->sub_id == HIDPP_SUBID_WIRELESS_DEVICE_CHANGE) - return "wireless-device-change"; - if (msg->sub_id == HIDPP_SUBID_ACL) - return "acl"; - if (msg->sub_id == HIDPP_SUBID_VOIP_TELEPHONY_EVENT) - return "voip-telephony-event"; - if (msg->sub_id == HIDPP_SUBID_LED) - return "led"; - if (msg->sub_id == HIDPP_SUBID_GESTURE_AND_AIR) - return "gesture-and-air"; - if (msg->sub_id == HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH) - return "touchpad-multi-touch"; - if (msg->sub_id == HIDPP_SUBID_TRACEABILITY) - return "traceability"; - if (msg->sub_id == HIDPP_SUBID_SET_REGISTER) - return "set-register"; - if (msg->sub_id == HIDPP_SUBID_GET_REGISTER) - return "get-register"; - if (msg->sub_id == HIDPP_SUBID_SET_LONG_REGISTER) - return "set-long-register"; - if (msg->sub_id == HIDPP_SUBID_GET_LONG_REGISTER) - return "get-long-register"; - if (msg->sub_id == HIDPP_SUBID_SET_VERY_LONG_REGISTER) - return "set-very-long-register"; - if (msg->sub_id == HIDPP_SUBID_GET_VERY_LONG_REGISTER) - return "get-very-long-register"; - if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) - return "error-msg"; - if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) - return "error-msg-v2"; - return NULL; -} - -gboolean -lu_hidpp_msg_is_reply (LuHidppMsg *msg1, LuHidppMsg *msg2) -{ - g_return_val_if_fail (msg1 != NULL, FALSE); - g_return_val_if_fail (msg2 != NULL, FALSE); - if (msg1->device_id != msg2->device_id && - msg1->device_id != HIDPP_DEVICE_ID_UNSET && - msg2->device_id != HIDPP_DEVICE_ID_UNSET) - return FALSE; - if (msg1->flags & LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID || - msg2->flags & LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID) - return TRUE; - if (msg1->sub_id != msg2->sub_id) - return FALSE; - if (msg1->flags & LU_HIDPP_MSG_FLAG_IGNORE_FNCT_ID || - msg2->flags & LU_HIDPP_MSG_FLAG_IGNORE_FNCT_ID) - return TRUE; - if (msg1->function_id != msg2->function_id) - return FALSE; - return TRUE; -} - -/* HID++ error */ -gboolean -lu_hidpp_msg_is_error (LuHidppMsg *msg, GError **error) -{ - g_return_val_if_fail (msg != NULL, FALSE); - if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) { - switch (msg->data[1]) { - case HIDPP_ERR_INVALID_SUBID: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "invalid SubID"); - break; - case HIDPP_ERR_INVALID_ADDRESS: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "invalid address"); - break; - case HIDPP_ERR_INVALID_VALUE: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "invalid value"); - break; - case HIDPP_ERR_CONNECT_FAIL: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "connection request failed"); - break; - case HIDPP_ERR_TOO_MANY_DEVICES: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_NO_SPACE, - "too many devices connected"); - break; - case HIDPP_ERR_ALREADY_EXISTS: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_EXISTS, - "already exists"); - break; - case HIDPP_ERR_BUSY: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_BUSY, - "busy"); - break; - case HIDPP_ERR_UNKNOWN_DEVICE: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "unknown device"); - break; - case HIDPP_ERR_RESOURCE_ERROR: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_HOST_UNREACHABLE, - "resource error"); - break; - case HIDPP_ERR_REQUEST_UNAVAILABLE: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_EXISTS, - "request not valid in current context"); - break; - case HIDPP_ERR_INVALID_PARAM_VALUE: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "request parameter has unsupported value"); - break; - case HIDPP_ERR_WRONG_PIN_CODE: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_CONNECTION_REFUSED, - "the pin code was wrong"); - break; - default: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "generic failure"); - } - return FALSE; - } - if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) { - switch (msg->data[1]) { - case HIDPP_ERROR_CODE_INVALID_ARGUMENT: - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "Invalid argument 0x%02x", - msg->data[2]); - break; - case HIDPP_ERROR_CODE_OUT_OF_RANGE: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "out of range"); - break; - case HIDPP_ERROR_CODE_HW_ERROR: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_BROKEN_PIPE, - "hardware error"); - break; - case HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "invalid feature index"); - break; - case HIDPP_ERROR_CODE_INVALID_FUNCTION_ID: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "invalid function ID"); - break; - case HIDPP_ERROR_CODE_BUSY: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_BUSY, - "busy"); - break; - case HIDPP_ERROR_CODE_UNSUPPORTED: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "unsupported"); - break; - default: - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "generic failure"); - break; - } - return FALSE; - } - return TRUE; -} - -void -lu_hidpp_msg_copy (LuHidppMsg *msg_dst, LuHidppMsg *msg_src) -{ - g_return_if_fail (msg_dst != NULL); - g_return_if_fail (msg_src != NULL); - memset (msg_dst->data, 0x00, sizeof(msg_dst->data)); - msg_dst->device_id = msg_src->device_id; - msg_dst->sub_id = msg_src->sub_id; - msg_dst->function_id = msg_src->function_id; - memcpy (msg_dst->data, msg_src->data, sizeof(msg_dst->data)); -} - -/* filter HID++1.0 messages */ -gboolean -lu_hidpp_msg_is_hidpp10_compat (LuHidppMsg *msg) -{ - g_return_val_if_fail (msg != NULL, FALSE); - if (msg->sub_id == 0x40 || - msg->sub_id == 0x41 || - msg->sub_id == 0x49 || - msg->sub_id == 0x4b || - msg->sub_id == 0x8f) { - return TRUE; - } - return FALSE; -} - -gboolean -lu_hidpp_msg_verify_swid (LuHidppMsg *msg) -{ - g_return_val_if_fail (msg != NULL, FALSE); - if ((msg->function_id & 0x0f) != LU_HIDPP_MSG_SW_ID) - return FALSE; - return TRUE; -} diff -Nru fwupd-1.0.9/plugins/unifying/lu-hidpp-msg.h fwupd-1.2.10/plugins/unifying/lu-hidpp-msg.h --- fwupd-1.0.9/plugins/unifying/lu-hidpp-msg.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-hidpp-msg.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __LU_HIDPP_MSG_H -#define __LU_HIDPP_MSG_H - -#include - -G_BEGIN_DECLS - -typedef enum { - LU_HIDPP_MSG_FLAG_NONE, - LU_HIDPP_MSG_FLAG_LONGER_TIMEOUT = 1 << 0, - LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID = 1 << 1, - LU_HIDPP_MSG_FLAG_IGNORE_FNCT_ID = 1 << 2, - LU_HIDPP_MSG_FLAG_IGNORE_SWID = 1 << 3, - /*< private >*/ - LU_HIDPP_MSG_FLAG_LAST -} LuHidppMsgFlags; - -typedef struct __attribute__((packed)) { - guint8 report_id; - guint8 device_id; - guint8 sub_id; - guint8 function_id; /* funcId:software_id */ - guint8 data[47]; /* maximum supported by Windows XP SP2 */ - /* not included in the packet sent to the hardware */ - guint32 flags; -} LuHidppMsg; - -/* this is specific to fwupd */ -#define LU_HIDPP_MSG_SW_ID 0x07 - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(LuHidppMsg, g_free); -#pragma clang diagnostic pop - -LuHidppMsg *lu_hidpp_msg_new (void); -void lu_hidpp_msg_copy (LuHidppMsg *msg_dst, - LuHidppMsg *msg_src); -gsize lu_hidpp_msg_get_payload_length (LuHidppMsg *msg); -gboolean lu_hidpp_msg_is_reply (LuHidppMsg *msg1, - LuHidppMsg *msg2); -gboolean lu_hidpp_msg_is_hidpp10_compat (LuHidppMsg *msg); -gboolean lu_hidpp_msg_is_error (LuHidppMsg *msg, - GError **error); -gboolean lu_hidpp_msg_verify_swid (LuHidppMsg *msg); - -const gchar *lu_hidpp_msg_dev_id_to_string (LuHidppMsg *msg); -const gchar *lu_hidpp_msg_rpt_id_to_string (LuHidppMsg *msg); -const gchar *lu_hidpp_msg_sub_id_to_string (LuHidppMsg *msg); -const gchar *lu_hidpp_msg_fcn_id_to_string (LuHidppMsg *msg); - -G_END_DECLS - -#endif /* __LU_HIDPP_MSG_H */ diff -Nru fwupd-1.0.9/plugins/unifying/lu-self-test.c fwupd-1.2.10/plugins/unifying/lu-self-test.c --- fwupd-1.0.9/plugins/unifying/lu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/lu-self-test.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include -#include - -#include "lu-common.h" - -static void -lu_common_func (void) -{ - guint8 u8; - guint16 u16; - g_autofree gchar *ver1 = NULL; - - u8 = lu_buffer_read_uint8 ("12"); - g_assert_cmpint (u8, ==, 0x12); - u16 = lu_buffer_read_uint16 ("1234"); - g_assert_cmpint (u16, ==, 0x1234); - - ver1 = lu_format_version (" A ", 0x87, 0x65, 0x4321); - g_assert_cmpstr (ver1, ==, "A87.65_B4321"); -} - -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 ("/unifying/common", lu_common_func); - return g_test_run (); -} diff -Nru fwupd-1.0.9/plugins/unifying/meson.build fwupd-1.2.10/plugins/unifying/meson.build --- fwupd-1.0.9/plugins/unifying/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,17 +1,24 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginUnifying"'] +install_data([ + 'unifying.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + + shared_module('fu_plugin_unifying', + fu_hash, sources : [ 'fu-plugin-unifying.c', - 'lu-common.c', - 'lu-context.c', - 'lu-device-bootloader.c', - 'lu-device-bootloader-nordic.c', - 'lu-device-bootloader-texas.c', - 'lu-device.c', - 'lu-device-peripheral.c', - 'lu-device-runtime.c', - 'lu-hidpp-msg.c', + 'fu-unifying-bootloader.c', + 'fu-unifying-bootloader-nordic.c', + 'fu-unifying-bootloader-texas.c', + 'fu-unifying-common.c', + 'fu-unifying-hidpp.c', + 'fu-unifying-hidpp-msg.c', + 'fu-unifying-peripheral.c', + 'fu-unifying-runtime.c', ], include_directories : [ include_directories('../..'), @@ -20,19 +27,22 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, - gudev, ], ) if get_option('tests') e = executable( 'unifying-self-test', + fu_hash, sources : [ - 'lu-self-test.c', - 'lu-common.c', + 'fu-unifying-self-test.c', + 'fu-unifying-common.c', ], include_directories : [ include_directories('../..'), @@ -40,10 +50,9 @@ ], dependencies : [ plugin_deps, - gudev, ], link_with : [ - fwupd, + libfwupdprivate, ], c_args : cargs, ) diff -Nru fwupd-1.0.9/plugins/unifying/README.md fwupd-1.2.10/plugins/unifying/README.md --- fwupd-1.0.9/plugins/unifying/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -10,12 +10,37 @@ This plugin will not work with the different "Nano" dongle (U0010) as it does not use the Unifying protocol. -Some bootloader protocol infomation was taken from the Mousejack[1] project, +Some bootloader protocol information was taken from the Mousejack[1] project, specifically logitech-usb-restore.py and unifying.py. Other documentation was 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 ------------ @@ -25,17 +50,5 @@ means the hardware keeps working while probing, and also allows us to detect paired devices. -Verification ------------- - -If you do not have Unifying hardware you can emulate writing firmware using: - - unifying-tool write file.hex -v --emulate=bootloader-nordic - -This can also be used to produce protocol data to the command line to compare -against USB dumps. This plugin should interact with the hardware exactly like -the Logitech-provided flashing tool, although only a few devices have been -tested. - [1] https://www.mousejack.com/ [2] https://pwr.github.io/Solaar/ diff -Nru fwupd-1.0.9/plugins/unifying/unifying.quirk fwupd-1.2.10/plugins/unifying/unifying.quirk --- fwupd-1.0.9/plugins/unifying/unifying.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/unifying/unifying.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,38 @@ +# Unifying Receiver +[DeviceInstanceId=HIDRAW\VEN_046D&DEV_C52B] +Plugin = unifying +Flags = is-receiver +VendorId=USB:0x046D +InstallDuration = 7 + +# Nordic +[DeviceInstanceId=USB\VID_046D&PID_AAAA] +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] +Plugin = unifying +Flags = is-bootloader,is-nordic +FirmwareSizeMin = 0x4000 +CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 7 + +# Texas +[DeviceInstanceId=USB\VID_046D&PID_AAAC] +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] +Plugin = unifying +Flags = is-bootloader,is-texas +FirmwareSizeMin = 0x4000 +CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 7 diff -Nru fwupd-1.0.9/plugins/upower/fu-plugin-upower.c fwupd-1.2.10/plugins/upower/fu-plugin-upower.c --- fwupd-1.0.9/plugins/upower/fu-plugin-upower.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/upower/fu-plugin-upower.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,16 +6,19 @@ #include "config.h" -#include "fu-plugin.h" #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)); } @@ -24,15 +26,17 @@ 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, @@ -41,35 +45,104 @@ "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; } -gboolean -fu_plugin_update_prepare (FuPlugin *plugin, - FuDevice *device, - GError **error) +static gboolean +fu_plugin_upower_check_percentage_level (FuPlugin *plugin) { FuPluginData *data = fu_plugin_get_data (plugin); - g_autoptr(GVariant) value = NULL; + 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; + } - /* can we only do this on AC power */ - if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC)) + /* 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; - value = g_dbus_proxy_get_cached_property (data->proxy, "OnBattery"); + } + 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 TRUE; + return FALSE; } - if (g_variant_get_boolean (value)) { + return g_variant_get_boolean (value); +} + +gboolean +fu_plugin_update_prepare (FuPlugin *plugin, + FwupdInstallFlags flags, + FuDevice *device, + GError **error) +{ + /* not all devices need this */ + if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC)) + return TRUE; + + /* 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, "Cannot install update " - "when not on AC power"); + "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_BATTERY_LEVEL_TOO_LOW, + "Cannot install update when battery " + "is not at least %d%% unless forced", + MINIMUM_BATTERY_PERCENTAGE); return FALSE; } return TRUE; diff -Nru fwupd-1.0.9/plugins/upower/meson.build fwupd-1.2.10/plugins/upower/meson.build --- fwupd-1.0.9/plugins/upower/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/upower/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -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 @@ ], install : true, install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], c_args : cargs, dependencies : [ plugin_deps, diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-plugin-wacomhid.c fwupd-1.2.10/plugins/wacomhid/fu-plugin-wacomhid.c --- fwupd-1.0.9/plugins/wacomhid/fu-plugin-wacomhid.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-plugin-wacomhid.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include "fu-plugin.h" -#include "fu-plugin-vfuncs.h" - -#include "fu-wac-device.h" - -gboolean -fu_plugin_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) -{ - g_autoptr(FuWacDevice) device = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; - device = fu_wac_device_new (usb_device); - fu_device_set_quirks (FU_DEVICE (device), fu_plugin_get_quirks (plugin)); - locker = fu_device_locker_new (device, error); - if (locker == NULL) - return FALSE; - fu_plugin_device_add (plugin, FU_DEVICE (device)); - return TRUE; -} - -gboolean -fu_plugin_update (FuPlugin *plugin, FuDevice *device, GBytes *blob_fw, - FwupdInstallFlags flags, GError **error) -{ - FuDevice *parent = fu_device_get_parent (device); - g_autoptr(FuDeviceLocker) locker = NULL; - locker = fu_device_locker_new (parent != NULL ? parent : device, error); - if (locker == NULL) - return FALSE; - return fu_device_write_firmware (device, blob_fw, error); -} diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-self-test.c fwupd-1.2.10/plugins/wacomhid/fu-self-test.c --- fwupd-1.0.9/plugins/wacomhid/fu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include -#include -#include - -#include "fu-common.h" -#include "fu-test.h" -#include "fu-wac-common.h" -#include "fu-wac-firmware.h" - -#include "fwupd-error.h" - -static void -fu_wac_firmware_parse_func (void) -{ - DfuElement *element; - DfuImage *image; - gboolean ret; - g_autofree gchar *fn = NULL; - g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); - g_autoptr(GBytes) blob_block = NULL; - g_autoptr(GBytes) bytes = NULL; - g_autoptr(GError) error = NULL; - - /* parse the test file */ - fn = fu_test_get_filename (TESTDATADIR, "test.wac"); - if (fn == NULL) { - g_test_skip ("no data file found"); - return; - } - bytes = fu_common_get_contents_bytes (fn, &error); - g_assert_no_error (error); - g_assert_nonnull (bytes); - ret = fu_wac_firmware_parse_data (firmware, bytes, - DFU_FIRMWARE_PARSE_FLAG_NONE, &error); - g_assert_no_error (error); - g_assert_true (ret); - - /* get image data */ - image = dfu_firmware_get_image (firmware, 0); - g_assert_nonnull (image); - element = dfu_image_get_element_default (image); - g_assert_nonnull (element); - - /* get block */ - blob_block = dfu_element_get_contents_chunk (element, 0x8008000, - 1024, &error); - g_assert_no_error (error); - g_assert_nonnull (blob_block); - fu_wac_buffer_dump ("IMG", FU_WAC_REPORT_ID_MODULE, - g_bytes_get_data (blob_block, NULL), - g_bytes_get_size (blob_block)); -} - -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); - - /* log everything */ - g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); - - /* tests go here */ - g_test_add_func ("/wac/firmware{parse}", fu_wac_firmware_parse_func); - return g_test_run (); -} - diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-common.c fwupd-1.2.10/plugins/wacomhid/fu-wac-common.c --- fwupd-1.0.9/plugins/wacomhid/fu-wac-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "fu-wac-common.h" - -guint32 -fu_wac_calculate_checksum32le (const guint8 *data, gsize len) -{ - guint32 csum = 0x0; - g_return_val_if_fail (len % 4 == 0, 0xff); - for (guint i = 0; i < len; i += 4) { - guint32 tmp; - memcpy (&tmp, &data[i], sizeof(guint32)); - csum += GUINT32_FROM_LE (tmp); - } - return GUINT32_TO_LE (csum); -} - -guint32 -fu_wac_calculate_checksum32le_bytes (GBytes *blob) -{ - gsize len = 0; - const guint8 *data = g_bytes_get_data (blob, &len); - return fu_wac_calculate_checksum32le (data, len); -} - -const gchar * -fu_wac_report_id_to_string (guint8 report_id) -{ - if (report_id == FU_WAC_REPORT_ID_FW_DESCRIPTOR) - return "FwDescriptor"; - if (report_id == FU_WAC_REPORT_ID_SWITCH_TO_FLASH_LOADER) - return "SwitchToFlashLoader"; - if (report_id == FU_WAC_REPORT_ID_QUIT_AND_RESET) - return "QuitAndReset"; - if (report_id == FU_WAC_REPORT_ID_READ_BLOCK_DATA) - return "ReadBlockData"; - if (report_id == FU_WAC_REPORT_ID_WRITE_BLOCK) - return "WriteBlock"; - if (report_id == FU_WAC_REPORT_ID_ERASE_BLOCK) - return "EraseBlock"; - if (report_id == FU_WAC_REPORT_ID_SET_READ_ADDRESS) - return "SetReadAddress"; - if (report_id == FU_WAC_REPORT_ID_GET_STATUS) - return "GetStatus"; - if (report_id == FU_WAC_REPORT_ID_UPDATE_RESET) - return "UpdateReset"; - if (report_id == FU_WAC_REPORT_ID_WRITE_WORD) - return "WriteWord"; - if (report_id == FU_WAC_REPORT_ID_GET_PARAMETERS) - return "GetParameters"; - if (report_id == FU_WAC_REPORT_ID_GET_FLASH_DESCRIPTOR) - return "GetFlashDescriptor"; - if (report_id == FU_WAC_REPORT_ID_GET_CHECKSUMS) - return "GetChecksums"; - if (report_id == FU_WAC_REPORT_ID_SET_CHECKSUM_FOR_BLOCK) - return "SetChecksumForBlock"; - if (report_id == FU_WAC_REPORT_ID_CALCULATE_CHECKSUM_FOR_BLOCK) - return "CalculateChecksumForBlock"; - if (report_id == FU_WAC_REPORT_ID_WRITE_CHECKSUM_TABLE) - return "WriteChecksumTable"; - if (report_id == FU_WAC_REPORT_ID_GET_CURRENT_FIRMWARE_IDX) - return "GetCurrentFirmwareIdx"; - if (report_id == FU_WAC_REPORT_ID_MODULE) - return "Module"; - return NULL; -} - -void -fu_wac_buffer_dump (const gchar *title, guint8 cmd, const guint8 *buf, gsize sz) -{ - if (g_getenv ("FWUPD_WAC_VERBOSE") == NULL) - return; - g_print ("%s %s (%" G_GSIZE_FORMAT "):\n", - title, fu_wac_report_id_to_string (cmd), 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 -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-common.h fwupd-1.2.10/plugins/wacomhid/fu-wac-common.h --- fwupd-1.0.9/plugins/wacomhid/fu-wac-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_WAC_HID_H -#define __FU_WAC_HID_H - -#include - -G_BEGIN_DECLS - -#define FU_WAC_PACKET_LEN 512 - -#define FU_WAC_REPORT_ID_COMMAND 0x01 -#define FU_WAC_REPORT_ID_STATUS 0x02 -#define FU_WAC_REPORT_ID_CONTROL 0x03 - -#define FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_MAIN 0x07 -#define FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_TOUCH 0x07 -#define FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_BLUETOOTH 0x16 - -#define FU_WAC_REPORT_ID_FW_DESCRIPTOR 0xcb /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_SWITCH_TO_FLASH_LOADER 0xcc /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_QUIT_AND_RESET 0xcd /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_READ_BLOCK_DATA 0xd1 /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_WRITE_BLOCK 0xd2 /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_ERASE_BLOCK 0xd3 /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_SET_READ_ADDRESS 0xd4 /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_GET_STATUS 0xd5 /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_UPDATE_RESET 0xd6 /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_WRITE_WORD 0xd7 /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_GET_PARAMETERS 0xd8 /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_GET_FLASH_DESCRIPTOR 0xd9 /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_GET_CHECKSUMS 0xda /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_SET_CHECKSUM_FOR_BLOCK 0xdb /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_CALCULATE_CHECKSUM_FOR_BLOCK 0xdc /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_WRITE_CHECKSUM_TABLE 0xde /* SET_FEATURE */ -#define FU_WAC_REPORT_ID_GET_CURRENT_FIRMWARE_IDX 0xe2 /* GET_FEATURE */ -#define FU_WAC_REPORT_ID_MODULE 0xe4 - -guint32 fu_wac_calculate_checksum32le (const guint8 *data, - gsize len); -guint32 fu_wac_calculate_checksum32le_bytes (GBytes *blob); -const gchar *fu_wac_report_id_to_string (guint8 report_id); -void fu_wac_buffer_dump (const gchar *title, - guint8 cmd, - const guint8 *buf, - gsize sz); - -G_END_DECLS - -#endif /* __FU_WAC_HID_H */ diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-device.c fwupd-1.2.10/plugins/wacomhid/fu-wac-device.c --- fwupd-1.0.9/plugins/wacomhid/fu-wac-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,921 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "fu-wac-device.h" -#include "fu-wac-common.h" -#include "fu-wac-firmware.h" -#include "fu-wac-module-bluetooth.h" -#include "fu-wac-module-touch.h" - -#include "dfu-chunked.h" -#include "dfu-common.h" -#include "dfu-firmware.h" - -typedef struct __attribute__((packed)) { - guint32 start_addr; - guint32 block_sz; - guint16 write_sz; /* bit 15 is write protection flag */ -} FuWacFlashDescriptor; - -typedef enum { - FU_WAC_STATUS_UNKNOWN = 0, - FU_WAC_STATUS_WRITING = 1 << 0, - FU_WAC_STATUS_ERASING = 1 << 1, - FU_WAC_STATUS_ERROR_WRITE = 1 << 2, - FU_WAC_STATUS_ERROR_ERASE = 1 << 3, - FU_WAC_STATUS_WRITE_PROTECTED = 1 << 4, - FU_WAC_STATUS_LAST -} FuWacStatus; - -#define FU_WAC_DEVICE_TIMEOUT 5000 /* ms */ - -struct _FuWacDevice -{ - FuUsbDevice parent_instance; - GPtrArray *flash_descriptors; - GArray *checksums; - guint32 status_word; - guint16 firmware_index; - guint16 loader_ver; - guint16 read_data_sz; - guint16 write_word_sz; - guint16 write_block_sz; /* usb transfer size */ - guint16 nr_flash_blocks; - guint16 configuration; -}; - -G_DEFINE_TYPE (FuWacDevice, fu_wac_device, FU_TYPE_USB_DEVICE) - -static GString * -fu_wac_device_status_to_string (guint32 status_word) -{ - GString *str = g_string_new (NULL); - if (status_word & FU_WAC_STATUS_WRITING) - g_string_append (str, "writing,"); - if (status_word & FU_WAC_STATUS_ERASING) - g_string_append (str, "erasing,"); - if (status_word & FU_WAC_STATUS_ERROR_WRITE) - g_string_append (str, "error-write,"); - if (status_word & FU_WAC_STATUS_ERROR_ERASE) - g_string_append (str, "error-erase,"); - if (status_word & FU_WAC_STATUS_WRITE_PROTECTED) - g_string_append (str, "write-protected,"); - if (str->len == 0) { - g_string_append (str, "none"); - return str; - } - g_string_truncate (str, str->len - 1); - return str; -} - -static gboolean -fu_wav_device_flash_descriptor_is_wp (const FuWacFlashDescriptor *fd) -{ - return fd->write_sz & 0x8000; -} - -static void -fu_wac_device_to_string (FuDevice *device, GString *str) -{ - GPtrArray *children; - FuWacDevice *self = FU_WAC_DEVICE (device); - g_autoptr(GString) status_str = NULL; - - g_string_append (str, " FuWacDevice:\n"); - if (self->firmware_index != 0xffff) { - g_string_append_printf (str, " fw-index: 0x%04x\n", - self->firmware_index); - } - if (self->loader_ver > 0) { - g_string_append_printf (str, " loader-ver: 0x%04x\n", - (guint) self->loader_ver); - } - if (self->read_data_sz > 0) { - g_string_append_printf (str, " read-data-sz: 0x%04x\n", - (guint) self->read_data_sz); - } - if (self->write_word_sz > 0) { - g_string_append_printf (str, " write-word-sz: 0x%04x\n", - (guint) self->write_word_sz); - } - if (self->write_block_sz > 0) { - g_string_append_printf (str, " write-block-sz: 0x%04x\n", - (guint) self->write_block_sz); - } - if (self->nr_flash_blocks > 0) { - g_string_append_printf (str, " nr-flash-blocks: 0x%04x\n", - (guint) self->nr_flash_blocks); - } - if (self->configuration != 0xffff) { - g_string_append_printf (str, " configuration: 0x%04x\n", - (guint) self->configuration); - } - for (guint i = 0; i < self->flash_descriptors->len; i++) { - FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); - g_string_append_printf (str, " flash-descriptor-%02u:\n", i); - g_string_append_printf (str, " start-addr:\t0x%08x\n", - (guint) fd->start_addr); - g_string_append_printf (str, " block-sz:\t0x%08x\n", - (guint) fd->block_sz); - g_string_append_printf (str, " write-sz:\t0x%04x\n", - (guint) fd->write_sz & ~0x8000); - g_string_append_printf (str, " protected:\t%s\n", - fu_wav_device_flash_descriptor_is_wp (fd) ? "yes" : "no"); - } - status_str = fu_wac_device_status_to_string (self->status_word); - g_string_append_printf (str, " status:\t\t%s\n", status_str->str); - - /* print children also */ - children = fu_device_get_children (device); - for (guint i = 0; i < children->len; i++) { - FuDevice *child = g_ptr_array_index (children, i); - g_autofree gchar *tmp = fu_device_to_string (FU_DEVICE (child)); - g_string_append (str, " FuWacDeviceChild:\n"); - g_string_append (str, tmp); - } -} - -gboolean -fu_wac_device_get_feature_report (FuWacDevice *self, - guint8 *buf, gsize bufsz, - FuWacDeviceFeatureFlags flags, - GError **error) -{ - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); - gsize sz = 0; - guint8 cmd = buf[0]; - - /* hit hardware */ - fu_wac_buffer_dump ("GET", cmd, buf, bufsz); - if (!g_usb_device_control_transfer (usb_device, - G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - HID_REPORT_GET, /* bRequest */ - HID_FEATURE | cmd, /* wValue */ - 0x0000, /* wIndex */ - buf, bufsz, &sz, - FU_WAC_DEVICE_TIMEOUT, - NULL, error)) { - g_prefix_error (error, "Failed to get feature report: "); - return FALSE; - } - fu_wac_buffer_dump ("GE2", cmd, buf, sz); - - /* check packet */ - if (flags && FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC == 0 && sz != bufsz) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "packet get bytes %" G_GSIZE_FORMAT - " expected %" G_GSIZE_FORMAT, - sz, bufsz); - return FALSE; - } - if (buf[0] != cmd) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "command response was %i expected %i", - buf[0], cmd); - return FALSE; - } - return TRUE; -} - -gboolean -fu_wac_device_set_feature_report (FuWacDevice *self, - guint8 *buf, gsize bufsz, - FuWacDeviceFeatureFlags flags, - GError **error) -{ - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); - gsize sz = 0; - guint8 cmd = buf[0]; - - /* hit hardware */ - fu_wac_buffer_dump ("SET", cmd, buf, bufsz); - if (g_getenv ("FWUPD_WAC_EMULATE") != NULL) - return TRUE; - if (!g_usb_device_control_transfer (usb_device, - G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - HID_REPORT_SET, /* bRequest */ - HID_FEATURE | cmd, /* wValue */ - 0x0000, /* wIndex */ - buf, bufsz, &sz, - FU_WAC_DEVICE_TIMEOUT, - NULL, error)) { - g_prefix_error (error, "Failed to set feature report: "); - return FALSE; - } - - /* check packet */ - if (flags && FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC == 0 && sz != bufsz) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "packet sent bytes %" G_GSIZE_FORMAT - " expected %" G_GSIZE_FORMAT, - sz, bufsz); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_wac_device_ensure_flash_descriptors (FuWacDevice *self, GError **error) -{ - gsize sz = (self->nr_flash_blocks * 10) + 1; - g_autofree guint8 *buf = g_malloc (sz); - - /* already done */ - if (self->flash_descriptors->len > 0) - return TRUE; - - /* hit hardware */ - memset (buf, 0xff, sz); - buf[0] = FU_WAC_REPORT_ID_GET_FLASH_DESCRIPTOR; - if (!fu_wac_device_get_feature_report (self, buf, sz, - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error)) - return FALSE; - - /* parse */ - for (guint i = 0; i < self->nr_flash_blocks; i++) { - FuWacFlashDescriptor *fd = g_new0 (FuWacFlashDescriptor, 1); - const guint blksz = sizeof(FuWacFlashDescriptor); - fd->start_addr = fu_common_read_uint32 (buf + (i * blksz) + 1, G_LITTLE_ENDIAN); - fd->block_sz = fu_common_read_uint32 (buf + (i * blksz) + 5, G_LITTLE_ENDIAN); - fd->write_sz = fu_common_read_uint16 (buf + (i * blksz) + 9, G_LITTLE_ENDIAN); - g_ptr_array_add (self->flash_descriptors, fd); - } - g_debug ("added %u flash descriptors", self->flash_descriptors->len); - return TRUE; -} - -static gboolean -fu_wac_device_ensure_status (FuWacDevice *self, GError **error) -{ - g_autoptr(GString) str = NULL; - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_STATUS, - [1 ... 4] = 0xff }; - - /* hit hardware */ - buf[0] = FU_WAC_REPORT_ID_GET_STATUS; - if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error)) - return FALSE; - - /* parse */ - self->status_word = fu_common_read_uint32 (buf + 1, G_LITTLE_ENDIAN); - str = fu_wac_device_status_to_string (self->status_word); - g_debug ("status now: %s", str->str); - return TRUE; -} - -static gboolean -fu_wac_device_ensure_checksums (FuWacDevice *self, GError **error) -{ - gsize sz = (self->nr_flash_blocks * 4) + 5; - guint32 updater_version; - g_autofree guint8 *buf = g_malloc (sz); - - /* hit hardware */ - memset (buf, 0xff, sz); - buf[0] = FU_WAC_REPORT_ID_GET_CHECKSUMS; - if (!fu_wac_device_get_feature_report (self, buf, sz, - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error)) - return FALSE; - - /* parse */ - updater_version = fu_common_read_uint32 (buf + 1, G_LITTLE_ENDIAN); - g_debug ("updater-version: %" G_GUINT32_FORMAT, updater_version); - - /* get block checksums */ - g_array_set_size (self->checksums, 0); - for (guint i = 0; i < self->nr_flash_blocks; i++) { - guint32 csum = fu_common_read_uint32 (buf + 5 + (i * 4), G_LITTLE_ENDIAN); - g_debug ("checksum block %02u: 0x%08x", i, (guint) csum); - g_array_append_val (self->checksums, csum); - } - g_debug ("added %u checksums", self->flash_descriptors->len); - - return TRUE; -} - - -static gboolean -fu_wac_device_ensure_firmware_index (FuWacDevice *self, GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_CURRENT_FIRMWARE_IDX, - [1 ... 2] = 0xff }; - - /* hit hardware */ - if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error)) - return FALSE; - - /* parse */ - self->firmware_index = fu_common_read_uint16 (buf + 1, G_LITTLE_ENDIAN); - return TRUE; -} - -static gboolean -fu_wac_device_ensure_parameters (FuWacDevice *self, GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_PARAMETERS, - [1 ... 12] = 0xff }; - - /* hit hardware */ - if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error)) - return FALSE; - - /* parse */ - self->loader_ver = fu_common_read_uint16 (buf + 1, G_LITTLE_ENDIAN); - self->read_data_sz = fu_common_read_uint16 (buf + 3, G_LITTLE_ENDIAN); - self->write_word_sz = fu_common_read_uint16 (buf + 5, G_LITTLE_ENDIAN); - self->write_block_sz = fu_common_read_uint16 (buf + 7, G_LITTLE_ENDIAN); - self->nr_flash_blocks = fu_common_read_uint16 (buf + 9, G_LITTLE_ENDIAN); - self->configuration = fu_common_read_uint16 (buf + 11, G_LITTLE_ENDIAN); - return TRUE; -} - -static gboolean -fu_wac_device_write_block (FuWacDevice *self, - guint32 addr, - GBytes *blob, - GError **error) -{ - const guint8 *tmp; - gsize bufsz = self->write_block_sz + 5; - gsize sz = 0; - g_autofree guint8 *buf = g_malloc (bufsz); - - /* check size */ - tmp = g_bytes_get_data (blob, &sz); - if (sz > self->write_block_sz) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "packet was too large at %" G_GSIZE_FORMAT " bytes", - sz); - return FALSE; - } - - /* build packet */ - memset (buf, 0xff, bufsz); - buf[0] = FU_WAC_REPORT_ID_WRITE_BLOCK; - fu_common_write_uint32 (buf + 1, addr, G_LITTLE_ENDIAN); - if (sz > 0) - memcpy (buf + 5, tmp, sz); - - /* hit hardware */ - return fu_wac_device_set_feature_report (self, buf, bufsz, - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error); -} - -static gboolean -fu_wac_device_erase_block (FuWacDevice *self, guint32 addr, GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_ERASE_BLOCK, - [1 ... 4] = 0xff }; - - /* build packet */ - fu_common_write_uint32 (buf + 1, addr, G_LITTLE_ENDIAN); - - /* hit hardware */ - return fu_wac_device_set_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error); -} - -gboolean -fu_wac_device_update_reset (FuWacDevice *self, GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_UPDATE_RESET, - [1 ... 4] = 0xff }; - - /* hit hardware */ - return fu_wac_device_set_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error); -} - -static gboolean -fu_wac_device_set_checksum_of_block (FuWacDevice *self, - guint16 block_nr, - guint32 checksum, - GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_SET_CHECKSUM_FOR_BLOCK, - [1 ... 6] = 0xff }; - - /* build packet */ - fu_common_write_uint16 (buf + 1, block_nr, G_LITTLE_ENDIAN); - fu_common_write_uint32 (buf + 3, checksum, G_LITTLE_ENDIAN); - - /* hit hardware */ - return fu_wac_device_set_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error); -} - -static gboolean -fu_wac_device_calculate_checksum_of_block (FuWacDevice *self, - guint16 block_nr, - GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_CALCULATE_CHECKSUM_FOR_BLOCK, - [1 ... 2] = 0xff }; - - /* build packet */ - fu_common_write_uint16 (buf + 1, block_nr, G_LITTLE_ENDIAN); - - /* hit hardware */ - return fu_wac_device_set_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error); -} - -static gboolean -fu_wac_device_write_checksum_table (FuWacDevice *self, GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_WRITE_CHECKSUM_TABLE, - [1 ... 4] = 0xff }; - - /* hit hardware */ - return fu_wac_device_set_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error); -} - -static gboolean -fu_wac_device_switch_to_flash_loader (FuWacDevice *self, GError **error) -{ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_SWITCH_TO_FLASH_LOADER, - [1] = 0x05, - [2] = 0x6a }; - - /* hit hardware */ - return fu_wac_device_set_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error); -} - -static gboolean -fu_wac_device_write_firmware (FuDevice *device, GBytes *blob, GError **error) -{ - DfuElement *element; - DfuImage *image; - FuWacDevice *self = FU_WAC_DEVICE (device); - gsize blocks_done = 0; - gsize blocks_total = 0; - g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); - g_autoptr(GHashTable) fd_blobs = NULL; - g_autofree guint32 *csum_local = NULL; - - /* load .wac file, including metadata */ - if (!fu_wac_firmware_parse_data (firmware, blob, - DFU_FIRMWARE_PARSE_FLAG_NONE, - error)) - return FALSE; - if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_SREC) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "expected firmware format is 'srec', got '%s'", - dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); - return FALSE; - } - - /* enter flash mode */ - if (!fu_wac_device_switch_to_flash_loader (self, error)) - return FALSE; - - /* get current selected device */ - if (!fu_wac_device_ensure_firmware_index (self, error)) - return FALSE; - - /* use the correct image from the firmware */ - image = dfu_firmware_get_image (firmware, self->firmware_index == 1 ? 1 : 0); - if (image == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no firmware image for index %" G_GUINT16_FORMAT, - self->firmware_index); - return FALSE; - } - element = dfu_image_get_element_default (image); - if (element == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no element in image %" G_GUINT16_FORMAT, - self->firmware_index); - return FALSE; - } - g_debug ("using element at addr 0x%0x", - (guint) dfu_element_get_address (element)); - - /* get firmware parameters (page sz and transfer sz) */ - if (!fu_wac_device_ensure_parameters (self, error)) - return FALSE; - - /* get the current flash descriptors */ - if (!fu_wac_device_ensure_flash_descriptors (self, error)) - return FALSE; - - /* get the updater protocol version */ - if (!fu_wac_device_ensure_checksums (self, error)) - return FALSE; - - /* clear all checksums of pages */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); - for (guint16 i = 0; i < self->flash_descriptors->len; i++) { - FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); - if (fu_wav_device_flash_descriptor_is_wp (fd)) - continue; - if (!fu_wac_device_set_checksum_of_block (self, i, 0x0, error)) - return FALSE; - } - - /* get the blobs for each chunk */ - fd_blobs = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) g_bytes_unref); - for (guint16 i = 0; i < self->flash_descriptors->len; i++) { - FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); - GBytes *blob_block; - g_autoptr(GBytes) blob_tmp = NULL; - - if (fu_wav_device_flash_descriptor_is_wp (fd)) - continue; - blob_tmp = dfu_element_get_contents_chunk (element, - fd->start_addr, - fd->block_sz, - NULL); - if (blob_tmp == NULL) - break; - blob_block = dfu_utils_bytes_pad (blob_tmp, fd->block_sz); - g_hash_table_insert (fd_blobs, fd, blob_block); - } - - /* checksum actions post-write */ - blocks_total = g_hash_table_size (fd_blobs) + 2; - - /* write the data into the flash page */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); - csum_local = g_new0 (guint32, self->flash_descriptors->len); - for (guint16 i = 0; i < self->flash_descriptors->len; i++) { - FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); - GBytes *blob_block; - g_autoptr(GPtrArray) chunks = NULL; - - /* if page is protected */ - if (fu_wav_device_flash_descriptor_is_wp (fd)) - continue; - - /* get data for page */ - blob_block = g_hash_table_lookup (fd_blobs, fd); - if (blob_block == NULL) - break; - - /* erase entire block */ - if (!fu_wac_device_erase_block (self, i, error)) - return FALSE; - - /* write block in chunks */ - chunks = dfu_chunked_new_from_bytes (blob_block, - fd->start_addr, - 0, /* page_sz */ - self->write_block_sz); - for (guint j = 0; j < chunks->len; j++) { - DfuChunkedPacket *pkt = g_ptr_array_index (chunks, j); - g_autoptr(GBytes) blob_chunk = g_bytes_new (pkt->data, pkt->data_sz); - if (!fu_wac_device_write_block (self, pkt->address, blob_chunk, error)) - return FALSE; - } - - /* calculate expected checksum and save to device RAM */ - csum_local[i] = fu_wac_calculate_checksum32le_bytes (blob_block); - g_debug ("block checksum %02u: 0x%08x", i, csum_local[i]); - if (!fu_wac_device_set_checksum_of_block (self, i, csum_local[i], error)) - return FALSE; - - /* update device progress */ - fu_device_set_progress_full (device, blocks_done++, blocks_total); - } - - /* calculate CRC inside device */ - for (guint16 i = 0; i < self->flash_descriptors->len; i++) { - if (!fu_wac_device_calculate_checksum_of_block (self, i, error)) - return FALSE; - } - - /* update device progress */ - fu_device_set_progress_full (device, blocks_done++, blocks_total); - - /* read all CRC of all pages and verify with local CRC */ - if (!fu_wac_device_ensure_checksums (self, error)) - return FALSE; - for (guint16 i = 0; i < self->flash_descriptors->len; i++) { - FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); - guint32 csum_rom; - - /* if page is protected */ - if (fu_wav_device_flash_descriptor_is_wp (fd)) - continue; - - /* no more written pages */ - if (g_hash_table_lookup (fd_blobs, fd) == NULL) - break; - - /* check checksum matches */ - csum_rom = g_array_index (self->checksums, guint32, i); - if (csum_rom != csum_local[i]) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed local checksum at block %u, " - "got 0x%08x expected 0x%08x", i, - (guint) csum_rom, (guint) csum_local[i]); - return FALSE; - } - g_debug ("matched checksum at block %u of 0x%08x", i, csum_rom); - } - - /* update device progress */ - fu_device_set_progress_full (device, blocks_done++, blocks_total); - - /* store host CRC into flash */ - if (!fu_wac_device_write_checksum_table (self, error)) - return FALSE; - - /* update progress */ - fu_device_set_progress_full (device, blocks_total, blocks_total); - - /* reboot, which switches the boot index of the firmware */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); - return fu_wac_device_update_reset (self, error); -} - -static gboolean -fu_wac_device_probe (FuUsbDevice *device, GError **error) -{ - const gchar *plugin_hints; - - /* devices have to be whitelisted */ - plugin_hints = fu_device_get_plugin_hints (FU_DEVICE (device)); - if (plugin_hints == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported with this device"); - return FALSE; - } - - /* hardware cannot respond to GetReport(DeviceFirmwareDescriptor) */ - if (g_strcmp0 (plugin_hints, "use-runtime-version") == 0) { - fu_device_add_flag (FU_DEVICE (device), - FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION); - } - - /* hardcoded */ - fu_device_add_icon (FU_DEVICE (device), "input-tablet"); - fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); - return TRUE; -} - -static gboolean -fu_wac_device_add_modules_bluetooth (FuWacDevice *self, GError **error) -{ - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); - g_autofree gchar *name = NULL; - g_autofree gchar *version = NULL; - g_autoptr(FuWacModule) module = NULL; - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_BLUETOOTH, - [1 ... 14] = 0xff }; - - buf[0] = FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_BLUETOOTH; - if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error)) { - g_prefix_error (error, "Failed to get GetFirmwareVersionBluetooth: "); - return FALSE; - } - - /* success */ - name = g_strdup_printf ("%s [Legacy Bluetooth Module]", - fu_device_get_name (FU_DEVICE (self))); - version = g_strdup_printf ("%x.%x", (guint) buf[2], (guint) buf[1]); - module = fu_wac_module_bluetooth_new (usb_device); - fu_device_add_child (FU_DEVICE (self), FU_DEVICE (module)); - fu_device_set_name (FU_DEVICE (module), name); - fu_device_set_version (FU_DEVICE (module), version); - return TRUE; -} - -static gboolean -fu_wac_device_add_modules_legacy (FuWacDevice *self, GError **error) -{ - g_autoptr(GError) error_bt = NULL; - - /* optional bluetooth */ - if (!fu_wac_device_add_modules_bluetooth (self, &error_bt)) - g_debug ("no bluetooth hardware: %s", error_bt->message); - - return TRUE; -} - -static gboolean -fu_wac_device_add_modules (FuWacDevice *self, GError **error) -{ - GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); - g_autofree gchar *version_bootloader = NULL; - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_FW_DESCRIPTOR, - [1 ... 31] = 0xff }; - - if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_NONE, - error)) { - g_prefix_error (error, "Failed to get DeviceFirmwareDescriptor: "); - return FALSE; - } - - /* verify bootloader is compatible */ - if (buf[1] != 0x01) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "bootloader major version not compatible"); - return FALSE; - } - - /* verify the number of submodules is possible */ - if (buf[3] > (512 - 4) / 4) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "number of submodules is impossible"); - return FALSE; - } - - /* bootloader version */ - version_bootloader = g_strdup_printf ("%u.%u", buf[1], buf[2]); - fu_device_set_version_bootloader (FU_DEVICE (self), version_bootloader); - - /* get versions of each submodule */ - for (guint8 i = 0; i < buf[3]; i++) { - guint8 fw_type = buf[(i * 4) + 4] & ~0x80; - g_autofree gchar *name = NULL; - g_autofree gchar *version = NULL; - g_autoptr(FuWacModule) module = NULL; - - /* version number is decimal */ - version = g_strdup_printf ("%u.%u", buf[(i * 4) + 5], buf[(i * 4) + 6]); - - switch (fw_type) { - case FU_WAC_MODULE_FW_TYPE_TOUCH: - module = fu_wac_module_touch_new (usb_device); - name = g_strdup_printf ("%s [Touch Module]", - fu_device_get_name (FU_DEVICE (self))); - fu_device_add_child (FU_DEVICE (self), FU_DEVICE (module)); - fu_device_set_name (FU_DEVICE (module), name); - fu_device_set_version (FU_DEVICE (module), version); - break; - case FU_WAC_MODULE_FW_TYPE_BLUETOOTH: - module = fu_wac_module_bluetooth_new (usb_device); - name = g_strdup_printf ("%s [Bluetooth Module]", - fu_device_get_name (FU_DEVICE (self))); - fu_device_add_child (FU_DEVICE (self), FU_DEVICE (module)); - fu_device_set_name (FU_DEVICE (module), name); - fu_device_set_version (FU_DEVICE (module), version); - break; - case FU_WAC_MODULE_FW_TYPE_MAIN: - fu_device_set_version (FU_DEVICE (self), version); - break; - default: - g_warning ("unknown submodule type 0x%0x", fw_type); - break; - } - } - return TRUE; -} - -static gboolean -fu_wac_device_open (FuUsbDevice *device, GError **error) -{ - FuWacDevice *self = FU_WAC_DEVICE (device); - GUsbDevice *usb_device = fu_usb_device_get_dev (device); - g_autoptr(GString) str = g_string_new (NULL); - - /* open device */ - if (!g_usb_device_claim_interface (usb_device, 0x00, /* HID */ - G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, - error)) { - g_prefix_error (error, "failed to claim HID interface: "); - return FALSE; - } - - /* get current status */ - if (!fu_wac_device_ensure_status (self, error)) - return FALSE; - - /* get version of each sub-module */ - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION)) { - if (!fu_wac_device_add_modules_legacy (self, error)) - return FALSE; - } else { - if (!fu_wac_device_add_modules (self, error)) - return FALSE; - } - - /* success */ - fu_wac_device_to_string (FU_DEVICE (self), str); - g_debug ("opened: %s", str->str); - return TRUE; -} - -static gboolean -fu_wac_device_close (FuUsbDevice *device, GError **error) -{ - GUsbDevice *usb_device = fu_usb_device_get_dev (device); - - /* reattach wacom.ko */ - if (!g_usb_device_release_interface (usb_device, 0x00, /* HID */ - G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, - error)) { - g_prefix_error (error, "failed to re-attach interface: "); - return FALSE; - } - - /* The hidcore subsystem uses a generic power_supply that has a deferred - * work item that will lock the device. When removing the power_supply, - * we take the lock, then cancel the work item which needs to take the - * lock too. This needs to be fixed in the kernel, but for the moment - * this should let the kernel unstick itself. */ - g_usleep (20 * 1000); - - /* success */ - return TRUE; -} - -static void -fu_wac_device_init (FuWacDevice *self) -{ - self->flash_descriptors = g_ptr_array_new_with_free_func (g_free); - self->checksums = g_array_new (FALSE, FALSE, sizeof(guint32)); - self->configuration = 0xffff; - self->firmware_index = 0xffff; -} - -static void -fu_wac_device_finalize (GObject *object) -{ - FuWacDevice *self = FU_WAC_DEVICE (object); - - g_ptr_array_unref (self->flash_descriptors); - g_array_unref (self->checksums); - - G_OBJECT_CLASS (fu_wac_device_parent_class)->finalize (object); -} - -static void -fu_wac_device_class_init (FuWacDeviceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); - FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); - object_class->finalize = fu_wac_device_finalize; - klass_device->write_firmware = fu_wac_device_write_firmware; - klass_device->to_string = fu_wac_device_to_string; - klass_usb_device->open = fu_wac_device_open; - klass_usb_device->close = fu_wac_device_close; - klass_usb_device->probe = fu_wac_device_probe; -} - -FuWacDevice * -fu_wac_device_new (GUsbDevice *usb_device) -{ - FuWacDevice *device = NULL; - device = g_object_new (FU_TYPE_WAC_DEVICE, - "usb-device", usb_device, - NULL); - return device; -} diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-device.h fwupd-1.2.10/plugins/wacomhid/fu-wac-device.h --- fwupd-1.0.9/plugins/wacomhid/fu-wac-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_WAC_DEVICE_H -#define __FU_WAC_DEVICE_H - -#include -#include - -#include "fu-plugin.h" - -G_BEGIN_DECLS - -#define FU_TYPE_WAC_DEVICE (fu_wac_device_get_type ()) -G_DECLARE_FINAL_TYPE (FuWacDevice, fu_wac_device, FU, WAC_DEVICE, FuUsbDevice) - -typedef enum { - FU_WAC_DEVICE_FEATURE_FLAG_NONE = 0, - FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC = 1 << 0, - FU_WAC_DEVICE_FEATURE_FLAG_LAST -} FuWacDeviceFeatureFlags; - -FuWacDevice *fu_wac_device_new (GUsbDevice *usb_device); -gboolean fu_wac_device_update_reset (FuWacDevice *self, - GError **error); -gboolean fu_wac_device_get_feature_report (FuWacDevice *self, - guint8 *buf, - gsize bufsz, - FuWacDeviceFeatureFlags flags, - GError **error); -gboolean fu_wac_device_set_feature_report (FuWacDevice *self, - guint8 *buf, - gsize bufsz, - FuWacDeviceFeatureFlags flags, - GError **error); - -G_END_DECLS - -#endif /* __FU_WAC_DEVICE_H */ diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-firmware.c fwupd-1.2.10/plugins/wacomhid/fu-wac-firmware.c --- fwupd-1.0.9/plugins/wacomhid/fu-wac-firmware.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-firmware.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,224 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "dfu-element.h" -#include "dfu-format-srec.h" -#include "dfu-image.h" - -#include "fu-wac-firmware.h" - -#include "fwupd-error.h" - -typedef struct { - guint32 addr; - guint32 sz; - guint32 prog_start_addr; -} DfuFirmwareWacHeaderRecord; - -/** - * fu_wac_firmware_parse_data: - * @firmware: a #DfuFirmware - * @bytes: data to parse - * @flags: some #DfuFirmwareParseFlags - * @error: a #GError, or %NULL - * - * Unpacks into a firmware object from DfuSe data. - * - * Returns: %TRUE for success - **/ -gboolean -fu_wac_firmware_parse_data (DfuFirmware *firmware, - GBytes *bytes, - DfuFirmwareParseFlags flags, - GError **error) -{ - gsize len; - guint8 *data; - g_auto(GStrv) lines = NULL; - g_autoptr(GString) image_buffer = NULL; - g_autofree gchar *data_str = NULL; - guint8 images_cnt = 0; - g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func (g_free); - - /* check the prefix (BE) */ - data = (guint8 *) g_bytes_get_data (bytes, &len); - if (memcmp (data, "WACOM", 5) != 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "invalid .wac prefix"); - return FALSE; - } - - /* parse each line */ - data_str = g_strndup ((const gchar *) data, len); - lines = g_strsplit (data_str, "\n", -1); - for (guint i = 0; lines[i] != NULL; i++) { - g_autofree gchar *cmd = g_strndup (lines[i], 2); - - /* remove windows line endings */ - g_strdelimit (lines[i], "\r", '\0'); - - /* Wacom-specific metadata */ - if (g_strcmp0 (cmd, "WA") == 0) { - guint cmdlen = strlen (lines[i]); - - /* header info record */ - if (memcmp (lines[i] + 2, "COM", 3) == 0) { - guint8 header_image_cnt = 0; - if (cmdlen != 40) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "invalid header, got %u bytes", - cmdlen); - return FALSE; - } - header_image_cnt = dfu_utils_buffer_parse_uint4 (lines[i] + 5); - for (guint j = 0; j < header_image_cnt; j++) { - DfuFirmwareWacHeaderRecord *hdr = g_new0 (DfuFirmwareWacHeaderRecord, 1); - hdr->addr = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 6); - hdr->sz = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 14); - g_ptr_array_add (header_infos, hdr); - g_debug ("header_fw%u_addr: 0x%x", j, hdr->addr); - g_debug ("header_fw%u_sz: 0x%x", j, hdr->sz); - } - continue; - } - - /* firmware headline record */ - if (cmdlen == 13) { - DfuFirmwareWacHeaderRecord *hdr; - guint8 idx = dfu_utils_buffer_parse_uint4 (lines[i] + 2); - if (idx == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "headline %u invalid", - idx); - return FALSE; - } - if (idx > header_infos->len) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "headline %u exceeds header count %u", - idx, header_infos->len); - return FALSE; - } - hdr = g_ptr_array_index (header_infos, idx - 1); - hdr->prog_start_addr = dfu_utils_buffer_parse_uint32 (lines[i] + 3); - if (hdr->prog_start_addr != hdr->addr) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "programming address 0x%x != " - "base address 0x%0x for idx %u", - hdr->prog_start_addr, - hdr->addr, - idx); - return FALSE; - } - g_debug ("programing-start-address: 0x%x", hdr->prog_start_addr); - continue; - } - - g_debug ("unknown Wacom-specific metadata"); - continue; - } - - /* start */ - if (g_strcmp0 (cmd, "S0") == 0) { - if (image_buffer != NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "duplicate S0 without S7"); - return FALSE; - } - image_buffer = g_string_new (NULL); - } - - /* these are things we want to include in the image */ - if (g_strcmp0 (cmd, "S0") == 0 || - g_strcmp0 (cmd, "S1") == 0 || - g_strcmp0 (cmd, "S2") == 0 || - g_strcmp0 (cmd, "S3") == 0 || - g_strcmp0 (cmd, "S5") == 0 || - g_strcmp0 (cmd, "S7") == 0 || - g_strcmp0 (cmd, "S8") == 0 || - g_strcmp0 (cmd, "S9") == 0) { - if (image_buffer == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "%s without S0", cmd); - return FALSE; - } - g_string_append_printf (image_buffer, "%s\n", lines[i]); - } - - /* end */ - if (g_strcmp0 (cmd, "S7") == 0) { - g_autoptr(GBytes) blob = NULL; - g_autoptr(DfuImage) image = dfu_image_new (); - DfuFirmwareWacHeaderRecord *hdr; - - /* get the correct relocated start address */ - if (images_cnt >= header_infos->len) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "%s without header", cmd); - return FALSE; - } - hdr = g_ptr_array_index (header_infos, images_cnt); - - /* parse SREC file and add as image */ - blob = g_bytes_new (image_buffer->str, image_buffer->len); - if (!dfu_image_from_srec (image, blob, hdr->addr, flags, error)) - return FALSE; - - /* the alt-setting is used for the firmware index */ - dfu_image_set_alt_setting (image, images_cnt); - dfu_firmware_add_image (firmware, image); - images_cnt++; - - /* clear the image buffer */ - g_string_free (image_buffer, TRUE); - image_buffer = NULL; - } - } - - /* verify data is complete */ - if (image_buffer != NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "truncated data: no S7"); - return FALSE; - } - - /* ensure this matched the header */ - if (header_infos->len != images_cnt) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "not enough images %u for header count %u", - images_cnt, - header_infos->len); - return FALSE; - } - - /* success */ - dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_SREC); - return TRUE; -} diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-firmware.h fwupd-1.2.10/plugins/wacomhid/fu-wac-firmware.h --- fwupd-1.0.9/plugins/wacomhid/fu-wac-firmware.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-firmware.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_WAC_FIRMWARE_H -#define __FU_WAC_FIRMWARE_H - -#include -#include - -#include "dfu-firmware.h" - -G_BEGIN_DECLS - -gboolean fu_wac_firmware_parse_data (DfuFirmware *firmware, - GBytes *bytes, - DfuFirmwareParseFlags flags, - GError **error); - -G_END_DECLS - -#endif /* __FU_WAC_FIRMWARE_H */ diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-module-bluetooth.c fwupd-1.2.10/plugins/wacomhid/fu-wac-module-bluetooth.c --- fwupd-1.0.9/plugins/wacomhid/fu-wac-module-bluetooth.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-module-bluetooth.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "fu-wac-common.h" -#include "fu-wac-device.h" -#include "fu-wac-module-bluetooth.h" - -#include "dfu-chunked.h" - -struct _FuWacModuleBluetooth -{ - FuWacModule parent_instance; -}; - -G_DEFINE_TYPE (FuWacModuleBluetooth, fu_wac_module_bluetooth, FU_TYPE_WAC_MODULE) - -#define FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ 256 -#define FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_START 0x3000 -#define FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_STOP 0x8000 - -typedef struct { - guint8 preamble[7]; - guint8 addr[3]; - guint8 crc; - guint8 cdata[FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ]; -} FuWacModuleBluetoothBlockData; - -static void -fu_wac_module_bluetooth_calculate_crc_byte (guint8 *crc, guint8 data) -{ - guint8 c[8]; - guint8 m[8]; - guint8 r[8]; - - /* find out what bits are set */ - for (guint i = 0; i < 8; i++) { - c[i] = (*crc & 1 << i) > 0; - m[i] = (data & 1 << i) > 0; - } - - /* do CRC on byte */ - r[7] = (c[7] ^ m[4] ^ c[3] ^ m[3] ^ c[4] ^ m[6] ^ c[1] ^ m[0]); - r[6] = (c[6] ^ m[5] ^ c[2] ^ m[4] ^ c[3] ^ m[7] ^ c[0] ^ m[1]); - r[5] = (c[5] ^ m[6] ^ c[1] ^ m[5] ^ c[2] ^ m[2]); - r[4] = (c[4] ^ m[7] ^ c[0] ^ m[6] ^ c[1] ^ m[3]); - r[3] = (m[7] ^ m[0] ^ c[7] ^ c[0] ^ m[3] ^ c[4] ^ m[6] ^ c[1]); - r[2] = (m[1] ^ c[6] ^ m[0] ^ c[7] ^ m[3] ^ c[4] ^ m[7] ^ c[0] ^ m[6] ^ c[1]); - r[1] = (m[2] ^ c[5] ^ m[1] ^ c[6] ^ m[4] ^ c[3] ^ m[7] ^ c[0]); - r[0] = (m[3] ^ c[4] ^ m[2] ^ c[5] ^ m[5] ^ c[2]); - - /* copy back into CRC */ - *crc = 0; - for (guint i = 0; i < 8; i++) { - if (r[i] == 0) - continue; - *crc |= (1 << i); - } -} - -static guint8 -fu_wac_module_bluetooth_calculate_crc (const guint8 *data, gsize sz) -{ - guint8 crc = 0; - for (gsize i = 0; i < sz; i++) - fu_wac_module_bluetooth_calculate_crc_byte (&crc, data[i]); - return crc; -} - -static GPtrArray * -fu_wac_module_bluetooth_parse_blocks (const guint8 *data, gsize sz, gboolean skip_user_data) -{ - const guint8 preamble[] = {0x02, 0x00, 0x0f, 0x06, 0x01, 0x08, 0x01}; - GPtrArray *blocks = g_ptr_array_new_with_free_func (g_free); - for (guint addr = 0x0; addr < sz; addr += FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ) { - FuWacModuleBluetoothBlockData *bd; - gsize cdata_sz = FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ; - - /* user data area */ - if (skip_user_data && - addr >= FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_START && - addr < FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_STOP) - continue; - - bd = g_new0 (FuWacModuleBluetoothBlockData, 1); - memcpy (bd->preamble, preamble, sizeof (preamble)); - bd->addr[0] = (addr >> 16) & 0xff; - bd->addr[1] = (addr >> 8) & 0xff; - bd->addr[2] = addr & 0xff; - memset (bd->cdata, 0xff, FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ); - - /* if file is not in multiples of payload size */ - if (addr + FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ >= sz) - cdata_sz = sz - addr; - memcpy (bd->cdata, data + addr, cdata_sz); - bd->crc = fu_wac_module_bluetooth_calculate_crc (bd->cdata, - FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ); - g_ptr_array_add (blocks, bd); - } - return blocks; -} - -static gboolean -fu_wac_module_bluetooth_write_firmware (FuDevice *device, GBytes *blob, GError **error) -{ - FuWacDevice *parent = FU_WAC_DEVICE (fu_device_get_parent (device)); - FuWacModule *self = FU_WAC_MODULE (device); - const guint8 *data; - gsize len = 0; - gsize blocks_total = 0; - const guint8 buf_start[] = { 0x00 }; - g_autoptr(GPtrArray) blocks = NULL; - g_autoptr(GBytes) blob_start = g_bytes_new_static (buf_start, 1); - - /* build each data packet */ - data = g_bytes_get_data (blob, &len); - blocks = fu_wac_module_bluetooth_parse_blocks (data, len, TRUE); - blocks_total = blocks->len + 2; - - /* start, which will erase the module */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); - if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_START, blob_start, error)) - return FALSE; - - /* update progress */ - fu_device_set_progress_full (device, 1, blocks_total); - - /* data */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); - for (guint i = 0; i < blocks->len; i++) { - FuWacModuleBluetoothBlockData *bd = g_ptr_array_index (blocks, i); - guint8 buf[256+11]; - g_autoptr(GBytes) blob_chunk = NULL; - - /* build data packet */ - memset (buf, 0xff, sizeof(buf)); - memcpy(&buf[0], bd->preamble, 7); - memcpy(&buf[7], bd->addr, 3); - buf[10] = bd->crc; - memcpy (&buf[11], bd->cdata, sizeof(bd->cdata)); - blob_chunk = g_bytes_new (buf, sizeof(buf)); - if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_DATA, - blob_chunk, error)) - return FALSE; - - /* update progress */ - fu_device_set_progress_full (device, i + 1, blocks_total); - } - - /* end */ - if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_END, NULL, error)) - return FALSE; - - /* update progress */ - fu_device_set_progress_full (device, blocks_total, blocks_total); - - /* reboot */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); - return fu_wac_device_update_reset (parent, error); -} - -static void -fu_wac_module_bluetooth_init (FuWacModuleBluetooth *self) -{ - fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); -} - -static void -fu_wac_module_bluetooth_class_init (FuWacModuleBluetoothClass *klass) -{ - FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); - klass_device->write_firmware = fu_wac_module_bluetooth_write_firmware; -} - -FuWacModule * -fu_wac_module_bluetooth_new (GUsbDevice *usb_device) -{ - FuWacModule *module = NULL; - module = g_object_new (FU_TYPE_WAC_MODULE_BLUETOOTH, - "usb-device", usb_device, - "fw-type", FU_WAC_MODULE_FW_TYPE_BLUETOOTH, - NULL); - return module; -} diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-module-bluetooth.h fwupd-1.2.10/plugins/wacomhid/fu-wac-module-bluetooth.h --- fwupd-1.0.9/plugins/wacomhid/fu-wac-module-bluetooth.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-module-bluetooth.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_WAC_MODULE_BLUETOOTH_H -#define __FU_WAC_MODULE_BLUETOOTH_H - -#include -#include - -#include "fu-wac-module.h" - -G_BEGIN_DECLS - -#define FU_TYPE_WAC_MODULE_BLUETOOTH (fu_wac_module_bluetooth_get_type ()) -G_DECLARE_FINAL_TYPE (FuWacModuleBluetooth, fu_wac_module_bluetooth, FU, WAC_MODULE_BLUETOOTH, FuWacModule) - -FuWacModule *fu_wac_module_bluetooth_new (GUsbDevice *usb_device); - -G_END_DECLS - -#endif /* __FU_WAC_MODULE_BLUETOOTH_H */ diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-module.c fwupd-1.2.10/plugins/wacomhid/fu-wac-module.c --- fwupd-1.0.9/plugins/wacomhid/fu-wac-module.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-module.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,358 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "fu-wac-module.h" -#include "fu-wac-common.h" -#include "fu-wac-device.h" - -#include "dfu-common.h" -#include "dfu-chunked.h" -#include "dfu-firmware.h" - -#define FU_WAC_MODLE_STATUS_OK 0 -#define FU_WAC_MODLE_STATUS_BUSY 1 -#define FU_WAC_MODLE_STATUS_ERR_CRC 2 -#define FU_WAC_MODLE_STATUS_ERR_CMD 3 -#define FU_WAC_MODLE_STATUS_ERR_HW_ACCESS_FAIL 4 -#define FU_WAC_MODLE_STATUS_ERR_FLASH_NO_SUPPORT 5 -#define FU_WAC_MODLE_STATUS_ERR_MODE_WRONG 6 -#define FU_WAC_MODLE_STATUS_ERR_MPU_NO_SUPPORT 7 -#define FU_WAC_MODLE_STATUS_ERR_VERSION_NO_SUPPORT 8 -#define FU_WAC_MODLE_STATUS_ERR_ERASE 9 -#define FU_WAC_MODLE_STATUS_ERR_WRITE 10 -#define FU_WAC_MODLE_STATUS_ERR_EXIT 11 -#define FU_WAC_MODLE_STATUS_ERR 12 -#define FU_WAC_MODLE_STATUS_ERR_INVALID_OP 13 -#define FU_WAC_MODLE_STATUS_ERR_WRONG_IMAGE 14 - -typedef struct { - GUsbDevice *usb_device; - guint8 fw_type; - guint8 command; - guint8 status; -} FuWacModulePrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (FuWacModule, fu_wac_module, FU_TYPE_DEVICE) -#define GET_PRIVATE(o) (fu_wac_module_get_instance_private (o)) - -enum { - PROP_0, - PROP_FW_TYPE, - PROP_USB_DEVICE, - PROP_LAST -}; - -static const gchar * -fu_wac_module_fw_type_to_string (guint8 fw_type) -{ - if (fw_type == FU_WAC_MODULE_FW_TYPE_TOUCH) - return "touch"; - if (fw_type == FU_WAC_MODULE_FW_TYPE_BLUETOOTH) - return "bluetooth"; - if (fw_type == FU_WAC_MODULE_FW_TYPE_EMR_CORRECTION) - return "emr-correction"; - if (fw_type == FU_WAC_MODULE_FW_TYPE_BLUETOOTH_HID) - return "bluetooth-hid"; - return NULL; -} - -static const gchar * -fu_wac_module_command_to_string (guint8 command) -{ - if (command == FU_WAC_MODULE_COMMAND_START) - return "start"; - if (command == FU_WAC_MODULE_COMMAND_DATA) - return "data"; - if (command == FU_WAC_MODULE_COMMAND_END) - return "end"; - return NULL; -} - -static const gchar * -fu_wac_module_status_to_string (guint8 status) -{ - if (status == FU_WAC_MODLE_STATUS_OK) - return "ok"; - if (status == FU_WAC_MODLE_STATUS_BUSY) - return "busy"; - if (status == FU_WAC_MODLE_STATUS_ERR_CRC) - return "err-crc"; - if (status == FU_WAC_MODLE_STATUS_ERR_CMD) - return "err-cmd"; - if (status == FU_WAC_MODLE_STATUS_ERR_HW_ACCESS_FAIL) - return "err-hw-access-fail"; - if (status == FU_WAC_MODLE_STATUS_ERR_FLASH_NO_SUPPORT) - return "err-flash-no-support"; - if (status == FU_WAC_MODLE_STATUS_ERR_MODE_WRONG) - return "err-mode-wrong"; - if (status == FU_WAC_MODLE_STATUS_ERR_MPU_NO_SUPPORT) - return "err-mpu-no-support"; - if (status == FU_WAC_MODLE_STATUS_ERR_VERSION_NO_SUPPORT) - return "erro-version-no-support"; - if (status == FU_WAC_MODLE_STATUS_ERR_ERASE) - return "err-erase"; - if (status == FU_WAC_MODLE_STATUS_ERR_WRITE) - return "err-write"; - if (status == FU_WAC_MODLE_STATUS_ERR_EXIT) - return "err-exit"; - if (status == FU_WAC_MODLE_STATUS_ERR) - return "err-err"; - if (status == FU_WAC_MODLE_STATUS_ERR_INVALID_OP) - return "err-invalid-op"; - if (status == FU_WAC_MODLE_STATUS_ERR_WRONG_IMAGE) - return "err-wrong-image"; - return NULL; -} - -static void -fu_wac_module_to_string (FuDevice *device, GString *str) -{ - FuWacModule *self = FU_WAC_MODULE (device); - FuWacModulePrivate *priv = GET_PRIVATE (self); - g_string_append (str, " FuWacSubModule:\n"); - g_string_append_printf (str, " fw-type:\t\t%s\n", - fu_wac_module_fw_type_to_string (priv->fw_type)); - g_string_append_printf (str, " status:\t\t%s\n", - fu_wac_module_status_to_string (priv->status)); - g_string_append_printf (str, " command:\t\t%s\n", - fu_wac_module_command_to_string (priv->command)); -} - -static gboolean -fu_wac_module_refresh (FuWacModule *self, GError **error) -{ - FuWacDevice *parent_device = FU_WAC_DEVICE (fu_device_get_parent (FU_DEVICE (self))); - FuWacModulePrivate *priv = GET_PRIVATE (self); - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_MODULE, - [1 ... FU_WAC_PACKET_LEN - 1] = 0xff }; - - /* get from hardware */ - if (!fu_wac_device_get_feature_report (parent_device, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC, - error)) { - g_prefix_error (error, "failed to refresh status: "); - return FALSE; - } - - /* check fw type */ - if (priv->fw_type != buf[1]) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Submodule GetFeature fw_Type invalid " - "got 0x%02x expected 0x%02x", - (guint) buf[1], (guint) priv->fw_type); - return FALSE; - } - - /* current phase */ - priv->command = buf[2]; - g_debug ("command: %s", fu_wac_module_command_to_string (priv->command)); - - /* current status */ - priv->status = buf[3]; - g_debug ("status: %s", fu_wac_module_status_to_string (priv->status)); - - /* success */ - return TRUE; -} - -gboolean -fu_wac_module_set_feature (FuWacModule *self, - guint8 command, - GBytes *blob, /* optional */ - GError **error) -{ - FuWacDevice *parent_device = FU_WAC_DEVICE (fu_device_get_parent (FU_DEVICE (self))); - FuWacModulePrivate *priv = GET_PRIVATE (self); - const guint8 *data; - gsize len = 0; - guint busy_poll_loops = 100; /* 1s */ - guint8 buf[] = { [0] = FU_WAC_REPORT_ID_MODULE, - [1] = priv->fw_type, - [2] = command, - [3 ... FU_WAC_PACKET_LEN - 1] = 0xff }; - - /* verify the size of the blob */ - if (blob != NULL) { - data = g_bytes_get_data (blob, &len); - if (len > 509) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Submodule SetFeature blob larger than " - "buffer %" G_GSIZE_FORMAT, len); - return FALSE; - } - } - - /* build packet */ - if (len > 0) - memcpy (&buf[3], data, len); - - /* tell the daemon the current status */ - switch (command) { - case FU_WAC_MODULE_COMMAND_START: - fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_ERASE); - break; - case FU_WAC_MODULE_COMMAND_DATA: - fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE); - break; - case FU_WAC_MODULE_COMMAND_END: - fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); - break; - default: - break; - } - - /* send to hardware */ - if (!fu_wac_device_set_feature_report (parent_device, buf, sizeof(buf), - FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC, - error)) { - g_prefix_error (error, "failed to set module feature: "); - return FALSE; - } - - /* special case StartProgram, as it can take much longer as it is - * erasing the blocks (15s) */ - if (command == FU_WAC_MODULE_COMMAND_START) - busy_poll_loops *= 15; - - /* wait for hardware */ - for (guint i = 0; i < busy_poll_loops; i++) { - if (!fu_wac_module_refresh (self, error)) - return FALSE; - if (priv->status == FU_WAC_MODLE_STATUS_BUSY) { - g_usleep (10000); /* 10ms */ - continue; - } - if (priv->status == FU_WAC_MODLE_STATUS_OK) - return TRUE; - } - - /* the hardware never responded */ - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Failed to SetFeature: %s", - fu_wac_module_status_to_string (priv->status)); - return FALSE; -} - -static void -fu_wac_module_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - FuWacModule *self = FU_WAC_MODULE (object); - FuWacModulePrivate *priv = GET_PRIVATE (self); - switch (prop_id) { - case PROP_FW_TYPE: - g_value_set_uint (value, priv->fw_type); - break; - case PROP_USB_DEVICE: - g_value_set_object (value, priv->usb_device); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -fu_wac_module_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - FuWacModule *self = FU_WAC_MODULE (object); - FuWacModulePrivate *priv = GET_PRIVATE (self); - switch (prop_id) { - case PROP_FW_TYPE: - priv->fw_type = g_value_get_uint (value); - break; - case PROP_USB_DEVICE: - g_set_object (&priv->usb_device, g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -fu_wac_module_init (FuWacModule *self) -{ -} - -static void -fu_wac_module_constructed (GObject *object) -{ - FuWacModule *self = FU_WAC_MODULE (object); - FuWacModulePrivate *priv = GET_PRIVATE (self); - g_autofree gchar *devid = NULL; - g_autofree gchar *platform_id = NULL; - g_autofree gchar *vendor_id = NULL; - - /* set vendor ID */ - vendor_id = g_strdup_printf ("USB:0x%04X", g_usb_device_get_vid (priv->usb_device)); - fu_device_set_vendor_id (FU_DEVICE (self), vendor_id); - - /* set USB platform ID automatically */ - platform_id = g_strdup_printf ("%s-%s", - g_usb_device_get_platform_id (priv->usb_device), - fu_wac_module_fw_type_to_string (priv->fw_type)); - fu_device_set_platform_id (FU_DEVICE (self), platform_id); - - /* append the firmware kind to the generated GUID */ - devid = g_strdup_printf ("USB\\VID_%04X&PID_%04X-%s", - g_usb_device_get_vid (priv->usb_device), - g_usb_device_get_pid (priv->usb_device), - fu_wac_module_fw_type_to_string (priv->fw_type)); - fu_device_add_guid (FU_DEVICE (self), devid); - - G_OBJECT_CLASS (fu_wac_module_parent_class)->constructed (object); -} - -static void -fu_wac_module_finalize (GObject *object) -{ - FuWacModule *self = FU_WAC_MODULE (object); - FuWacModulePrivate *priv = GET_PRIVATE (self); - if (priv->usb_device != NULL) - g_object_unref (priv->usb_device); - G_OBJECT_CLASS (fu_wac_module_parent_class)->finalize (object); -} - -static void -fu_wac_module_class_init (FuWacModuleClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); - - /* properties */ - object_class->get_property = fu_wac_module_get_property; - object_class->set_property = fu_wac_module_set_property; - pspec = g_param_spec_object ("usb-device", NULL, NULL, - G_USB_TYPE_DEVICE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_NAME); - g_object_class_install_property (object_class, PROP_USB_DEVICE, pspec); - pspec = g_param_spec_uint ("fw-type", NULL, NULL, - 0, G_MAXUINT, 0, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_NAME); - g_object_class_install_property (object_class, PROP_FW_TYPE, pspec); - - object_class->constructed = fu_wac_module_constructed; - object_class->finalize = fu_wac_module_finalize; - klass_device->to_string = fu_wac_module_to_string; -} diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-module.h fwupd-1.2.10/plugins/wacomhid/fu-wac-module.h --- fwupd-1.0.9/plugins/wacomhid/fu-wac-module.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-module.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_WAC_MODULE_H -#define __FU_WAC_MODULE_H - -#include - -#include "fu-plugin.h" - -G_BEGIN_DECLS - -#define FU_TYPE_WAC_MODULE (fu_wac_module_get_type ()) -G_DECLARE_DERIVABLE_TYPE (FuWacModule, fu_wac_module, FU, WAC_MODULE, FuDevice) - -struct _FuWacModuleClass -{ - FuDeviceClass parent_class; -}; - -#define FU_WAC_MODULE_FW_TYPE_TOUCH 0x00 -#define FU_WAC_MODULE_FW_TYPE_BLUETOOTH 0x01 -#define FU_WAC_MODULE_FW_TYPE_EMR_CORRECTION 0x02 -#define FU_WAC_MODULE_FW_TYPE_BLUETOOTH_HID 0x03 -#define FU_WAC_MODULE_FW_TYPE_MAIN 0x3f - -#define FU_WAC_MODULE_COMMAND_START 0x01 -#define FU_WAC_MODULE_COMMAND_DATA 0x02 -#define FU_WAC_MODULE_COMMAND_END 0x03 - -gboolean fu_wac_module_set_feature (FuWacModule *self, - guint8 command, - GBytes *blob, - GError **error); - -G_END_DECLS - -#endif /* __FU_WAC_MODULE_H */ diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-module-touch.c fwupd-1.2.10/plugins/wacomhid/fu-wac-module-touch.c --- fwupd-1.0.9/plugins/wacomhid/fu-wac-module-touch.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-module-touch.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include - -#include "fu-wac-device.h" -#include "fu-wac-module-touch.h" - -#include "dfu-chunked.h" - -struct _FuWacModuleTouch -{ - FuWacModule parent_instance; -}; - -G_DEFINE_TYPE (FuWacModuleTouch, fu_wac_module_touch, FU_TYPE_WAC_MODULE) - -static gboolean -fu_wac_module_touch_write_firmware (FuDevice *device, GBytes *blob, GError **error) -{ - FuWacDevice *parent = FU_WAC_DEVICE (fu_device_get_parent (device)); - FuWacModule *self = FU_WAC_MODULE (device); - const guint8 *data; - gsize blocks_total = 0; - gsize len = 0; - g_autoptr(GPtrArray) chunks = NULL; - - /* build each data packet */ - data = g_bytes_get_data (blob, &len); - if (len % 128 != 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "firmware has to be padded to 128b"); - return FALSE; - } - chunks = dfu_chunked_new (data, (guint32) len, - 0x0, /* addr_start */ - 0x0, /* page_sz */ - 128); /* packet_sz */ - blocks_total = chunks->len + 2; - - /* start, which will erase the module */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); - if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_START, NULL, error)) - return FALSE; - - /* update progress */ - fu_device_set_progress_full (device, 1, blocks_total); - - /* data */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); - for (guint i = 0; i < chunks->len; i++) { - DfuChunkedPacket *pkt = g_ptr_array_index (chunks, i); - guint8 buf[128+7]; - g_autoptr(GBytes) blob_chunk = NULL; - - /* build G11T data packet */ - memset (buf, 0xff, sizeof(buf)); - buf[0] = 0x01; /* writing */ - fu_common_write_uint32 (&buf[1], pkt->address, G_LITTLE_ENDIAN); - buf[5] = pkt->idx; - memcpy (&buf[6], pkt->data, pkt->data_sz); - blob_chunk = g_bytes_new (buf, sizeof(buf)); - if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_DATA, - blob_chunk, error)) - return FALSE; - - /* update progress */ - fu_device_set_progress_full (device, i + 1, blocks_total); - } - - /* end */ - if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_END, NULL, error)) - return FALSE; - - /* update progress */ - fu_device_set_progress_full (device, blocks_total, blocks_total); - - /* reboot */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); - return fu_wac_device_update_reset (parent, error); -} - -static void -fu_wac_module_touch_init (FuWacModuleTouch *self) -{ - fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); -} - -static void -fu_wac_module_touch_class_init (FuWacModuleTouchClass *klass) -{ - FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); - klass_device->write_firmware = fu_wac_module_touch_write_firmware; -} - -FuWacModule * -fu_wac_module_touch_new (GUsbDevice *usb_device) -{ - FuWacModule *module = NULL; - module = g_object_new (FU_TYPE_WAC_MODULE_TOUCH, - "usb-device", usb_device, - "fw-type", FU_WAC_MODULE_FW_TYPE_TOUCH, - NULL); - return module; -} diff -Nru fwupd-1.0.9/plugins/wacomhid/fu-wac-module-touch.h fwupd-1.2.10/plugins/wacomhid/fu-wac-module-touch.h --- fwupd-1.0.9/plugins/wacomhid/fu-wac-module-touch.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/fu-wac-module-touch.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef __FU_WAC_MODULE_TOUCH_H -#define __FU_WAC_MODULE_TOUCH_H - -#include -#include - -#include "fu-wac-module.h" - -G_BEGIN_DECLS - -#define FU_TYPE_WAC_MODULE_TOUCH (fu_wac_module_touch_get_type ()) -G_DECLARE_FINAL_TYPE (FuWacModuleTouch, fu_wac_module_touch, FU, WAC_MODULE_TOUCH, FuWacModule) - -FuWacModule *fu_wac_module_touch_new (GUsbDevice *usb_device); - -G_END_DECLS - -#endif /* __FU_WAC_MODULE_TOUCH_H */ diff -Nru fwupd-1.0.9/plugins/wacomhid/meson.build fwupd-1.2.10/plugins/wacomhid/meson.build --- fwupd-1.0.9/plugins/wacomhid/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/meson.build 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginWac"'] - -install_data(['wac-intuos.quirk'], - install_dir: join_paths(datadir, 'fwupd', 'quirks.d') -) - -shared_module('fu_plugin_wacomhid', - sources : [ - 'fu-wac-common.c', - 'fu-wac-device.c', - 'fu-wac-firmware.c', - 'fu-wac-module.c', - 'fu-wac-module-bluetooth.c', - 'fu-wac-module-touch.c', - 'fu-plugin-wacomhid.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, - ], -) - -if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'tests') - cargs += '-DTESTDATADIR="' + testdatadir + '"' - e = executable( - 'wacomhid-self-test', - sources : [ - 'fu-self-test.c', - 'fu-wac-common.c', - 'fu-wac-firmware.c', - ], - include_directories : [ - include_directories('..'), - include_directories('../dfu'), - include_directories('../..'), - include_directories('../../libfwupd'), - include_directories('../../src'), - ], - dependencies : [ - appstream_glib, - gio, - gusb, - libm, - ], - link_with : [ - dfu, - fwupd, - libfwupdprivate, - ], - c_args : cargs - ) - test('wacomhid-self-test', e) -endif diff -Nru fwupd-1.0.9/plugins/wacomhid/README.md fwupd-1.2.10/plugins/wacomhid/README.md --- fwupd-1.0.9/plugins/wacomhid/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -Wacom HID Support -================= - -Introduction ------------- - -Wacom provides interactive pen displays, pen tablets, and styluses to equip and -inspire everyone make the world a more creative place. - -From 2016 Wacom has been using a HID-based proprietary flashing algorithm which -has been documented by support team at Wacom and provided under NDA under the -understanding it would be used to build a plugin under a LGPLv2+ licence. - -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. diff -Nru fwupd-1.0.9/plugins/wacomhid/wac-intuos.quirk fwupd-1.2.10/plugins/wacomhid/wac-intuos.quirk --- fwupd-1.0.9/plugins/wacomhid/wac-intuos.quirk 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/plugins/wacomhid/wac-intuos.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -[FuWacDevice] - -# MobileStudio Pro 13 (touch) [DTH-W1320] -USB\VID_056A&PID_034A=use-runtime-version - -# MobileStudio Pro 16 (touch) [DTH-W1620] -USB\VID_056A&PID_034B=use-runtime-version - -# MobileStudio Pro 13 (pen/pad) [DTH-W1320] -USB\VID_056A&PID_034D=use-runtime-version - -# MobileStudio Pro 16 (pen/pad) [DTH-W1620] -USB\VID_056A&PID_034E=use-runtime-version - -# Intuos Pro medium (2nd-gen BT) [PTH-660] -USB\VID_056A&PID_0360=use-runtime-version - -# Intuos Pro large (2nd-gen BT) [PTH-860] -USB\VID_056A&PID_0361=use-runtime-version - -# Intuos BT S (3rd-gen BT) [CTL-4100WL] -USB\VID_056A&PID_0377=use-runtime-version - -# Intuos BT M (3rd-gen BT) [CTL-6100WL] -USB\VID_056A&PID_0379=use-runtime-version - -# Cintiq Pro 13 (pen/pad) [DTH-1320] -USB\VID_056A&PID_034F=use-runtime-version - -# Cintiq Pro 16 (pen/pad) [DTH-1620] -USB\VID_056A&PID_0350=use-runtime-version - -# Cintiq Pro 13 (touch) [DTH-1320] -USB\VID_056A&PID_0353=use-runtime-version - -# Cintiq Pro 16 (touch) [DTH-1620] -USB\VID_056A&PID_0354=use-runtime-version - -# Intuos Pro medium (2nd-gen USB) [PTH-660] -USB\VID_056A&PID_0357=use-runtime-version - -# Intuos Pro large (2nd-gen USB) [PTH-860] -USB\VID_056A&PID_0358=use-runtime-version - -# Pen [DTH-1152] -USB\VID_056A&PID_035A=use-runtime-version - -# Touch [DTH-1152] -USB\VID_056A&PID_0368=use-runtime-version - -# Intuos S 3rd-gen (USB) [CTL-4100] -USB\VID_056A&PID_0374=use-runtime-version - -# Intuos M 3rd-gen (USB) [NA] -USB\VID_056A&PID_0375=use-runtime-version - -# Intuos BT S 3rd-gen (USB) [CTL-4100WL] -USB\VID_056A&PID_0376=use-runtime-version - -# Intuos BT M 3rd-gen (USB) [CTL-6100WL] -USB\VID_056A&PID_0378=use-runtime-version diff -Nru fwupd-1.0.9/plugins/wacom-raw/data/hid-recorder.txt fwupd-1.2.10/plugins/wacom-raw/data/hid-recorder.txt --- fwupd-1.0.9/plugins/wacom-raw/data/hid-recorder.txt 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/data/hid-recorder.txt 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/wacom-raw/fu-plugin-wacom-raw.c fwupd-1.2.10/plugins/wacom-raw/fu-plugin-wacom-raw.c --- fwupd-1.0.9/plugins/wacom-raw/fu-plugin-wacom-raw.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-plugin-wacom-raw.c 2019-07-15 18:25:54.000000000 +0000 @@ -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_instance_id (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_instance_id (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, flags, error); +} diff -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-aes-device.c fwupd-1.2.10/plugins/wacom-raw/fu-wacom-aes-device.c --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-aes-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-aes-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -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", FWUPD_VERSION_FORMAT_PAIR); + } 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, FWUPD_VERSION_FORMAT_PAIR); + + /* 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 *devid = NULL; + devid = g_strdup_printf ("WACOM\\HWID_%04X", self->hwid); + fu_device_add_instance_id (device, devid); + } + } + + /* 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 -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-aes-device.h fwupd-1.2.10/plugins/wacom-raw/fu-wacom-aes-device.h --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-aes-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-aes-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-common.c fwupd-1.2.10/plugins/wacom-raw/fu-wacom-common.c --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-common.h fwupd-1.2.10/plugins/wacom-raw/fu-wacom-common.h --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-device.c fwupd-1.2.10/plugins/wacom-raw/fu-wacom-device.c --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,423 @@ +/* + * 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_instance_id (FU_DEVICE (self), "WacomEMR_W9013"); + return TRUE; + } + + /* W9021 */ + if (rsp.resp == 0x45) { + fu_device_add_instance_id (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, + FwupdInstallFlags flags, + GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS (device); + DfuElement *element; + DfuImage *image; + GBytes *fw_new; + g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(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 -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-device.h fwupd-1.2.10/plugins/wacom-raw/fu-wacom-device.h --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-emr-device.c fwupd-1.2.10/plugins/wacom-raw/fu-wacom-emr-device.c --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-emr-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-emr-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -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", FWUPD_VERSION_FORMAT_PAIR); + } 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, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_PAIR); + } + + /* 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_instance_id (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_instance_id (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 -Nru fwupd-1.0.9/plugins/wacom-raw/fu-wacom-emr-device.h fwupd-1.2.10/plugins/wacom-raw/fu-wacom-emr-device.h --- fwupd-1.0.9/plugins/wacom-raw/fu-wacom-emr-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/fu-wacom-emr-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/plugins/wacom-raw/meson.build fwupd-1.2.10/plugins/wacom-raw/meson.build --- fwupd-1.0.9/plugins/wacom-raw/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/wacom-raw/README.md fwupd-1.2.10/plugins/wacom-raw/README.md --- fwupd-1.0.9/plugins/wacom-raw/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/wacom-raw/wacom-raw.quirk fwupd-1.2.10/plugins/wacom-raw/wacom-raw.quirk --- fwupd-1.0.9/plugins/wacom-raw/wacom-raw.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-raw/wacom-raw.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/wacom-usb/data/lsusb.txt fwupd-1.2.10/plugins/wacom-usb/data/lsusb.txt --- fwupd-1.0.9/plugins/wacom-usb/data/lsusb.txt 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/data/lsusb.txt 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/plugins/wacom-usb/fu-plugin-wacom-usb.c fwupd-1.2.10/plugins/wacom-usb/fu-plugin-wacom-usb.c --- fwupd-1.0.9/plugins/wacom-usb/fu-plugin-wacom-usb.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-plugin-wacom-usb.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-wac-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.wacom.usb"); +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuWacDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + dev = fu_wac_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} + +gboolean +fu_plugin_update (FuPlugin *plugin, FuDevice *device, GBytes *blob_fw, + FwupdInstallFlags flags, GError **error) +{ + FuDevice *parent = fu_device_get_parent (device); + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (parent != NULL ? parent : device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, flags, error); +} + +static FuDevice * +fu_plugin_wacom_usb_get_device (GPtrArray *devices) +{ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index (devices, i); + if (FU_IS_WAC_DEVICE (dev)) + return dev; + } + return NULL; +} + +gboolean +fu_plugin_composite_cleanup (FuPlugin *plugin, + GPtrArray *devices, + GError **error) +{ + FuDevice *device = fu_plugin_wacom_usb_get_device (devices); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* not us */ + if (device == NULL) + return TRUE; + + /* reboot, which switches the boot index of the firmware */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + return fu_wac_device_update_reset (FU_WAC_DEVICE (device), error); +} diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-self-test.c fwupd-1.2.10/plugins/wacom-usb/fu-self-test.c --- fwupd-1.0.9/plugins/wacom-usb/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-common.h" +#include "fu-test.h" +#include "fu-wac-common.h" +#include "fu-wac-firmware.h" + +#include "fwupd-error.h" + +static void +fu_wac_firmware_parse_func (void) +{ + DfuElement *element; + DfuImage *image; + gboolean ret; + g_autofree gchar *fn = NULL; + g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(GBytes) blob_block = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + + /* parse the test file */ + fn = fu_test_get_filename (TESTDATADIR, "test.wac"); + if (fn == NULL) { + g_test_skip ("no data file found"); + return; + } + bytes = fu_common_get_contents_bytes (fn, &error); + g_assert_no_error (error); + g_assert_nonnull (bytes); + ret = fu_wac_firmware_parse_data (firmware, bytes, + DFU_FIRMWARE_PARSE_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert_true (ret); + + /* get image data */ + image = dfu_firmware_get_image (firmware, 0); + g_assert_nonnull (image); + element = dfu_image_get_element_default (image); + g_assert_nonnull (element); + + /* get block */ + blob_block = dfu_element_get_contents_chunk (element, 0x8008000, + 1024, &error); + g_assert_no_error (error); + g_assert_nonnull (blob_block); + fu_wac_buffer_dump ("IMG", FU_WAC_REPORT_ID_MODULE, + g_bytes_get_data (blob_block, NULL), + g_bytes_get_size (blob_block)); +} + +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); + + /* log everything */ + g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); + + /* tests go here */ + g_test_add_func ("/wac/firmware{parse}", fu_wac_firmware_parse_func); + return g_test_run (); +} + diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-common.c fwupd-1.2.10/plugins/wacom-usb/fu-wac-common.c --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-wac-common.h" + +guint32 +fu_wac_calculate_checksum32le (const guint8 *data, gsize len) +{ + guint32 csum = 0x0; + g_return_val_if_fail (len % 4 == 0, 0xff); + for (guint i = 0; i < len; i += 4) { + guint32 tmp; + memcpy (&tmp, &data[i], sizeof(guint32)); + csum += GUINT32_FROM_LE (tmp); + } + return GUINT32_TO_LE (csum); +} + +guint32 +fu_wac_calculate_checksum32le_bytes (GBytes *blob) +{ + gsize len = 0; + const guint8 *data = g_bytes_get_data (blob, &len); + return fu_wac_calculate_checksum32le (data, len); +} + +const gchar * +fu_wac_report_id_to_string (guint8 report_id) +{ + if (report_id == FU_WAC_REPORT_ID_FW_DESCRIPTOR) + return "FwDescriptor"; + if (report_id == FU_WAC_REPORT_ID_SWITCH_TO_FLASH_LOADER) + return "SwitchToFlashLoader"; + if (report_id == FU_WAC_REPORT_ID_QUIT_AND_RESET) + return "QuitAndReset"; + if (report_id == FU_WAC_REPORT_ID_READ_BLOCK_DATA) + return "ReadBlockData"; + if (report_id == FU_WAC_REPORT_ID_WRITE_BLOCK) + return "WriteBlock"; + if (report_id == FU_WAC_REPORT_ID_ERASE_BLOCK) + return "EraseBlock"; + if (report_id == FU_WAC_REPORT_ID_SET_READ_ADDRESS) + return "SetReadAddress"; + if (report_id == FU_WAC_REPORT_ID_GET_STATUS) + return "GetStatus"; + if (report_id == FU_WAC_REPORT_ID_UPDATE_RESET) + return "UpdateReset"; + if (report_id == FU_WAC_REPORT_ID_WRITE_WORD) + return "WriteWord"; + if (report_id == FU_WAC_REPORT_ID_GET_PARAMETERS) + return "GetParameters"; + if (report_id == FU_WAC_REPORT_ID_GET_FLASH_DESCRIPTOR) + return "GetFlashDescriptor"; + if (report_id == FU_WAC_REPORT_ID_GET_CHECKSUMS) + return "GetChecksums"; + if (report_id == FU_WAC_REPORT_ID_SET_CHECKSUM_FOR_BLOCK) + return "SetChecksumForBlock"; + if (report_id == FU_WAC_REPORT_ID_CALCULATE_CHECKSUM_FOR_BLOCK) + return "CalculateChecksumForBlock"; + if (report_id == FU_WAC_REPORT_ID_WRITE_CHECKSUM_TABLE) + return "WriteChecksumTable"; + if (report_id == FU_WAC_REPORT_ID_GET_CURRENT_FIRMWARE_IDX) + return "GetCurrentFirmwareIdx"; + if (report_id == FU_WAC_REPORT_ID_MODULE) + return "Module"; + return NULL; +} + +void +fu_wac_buffer_dump (const gchar *title, guint8 cmd, const guint8 *buf, gsize sz) +{ + g_autofree gchar *tmp = NULL; + if (g_getenv ("FWUPD_WACOM_USB_VERBOSE") == NULL) + return; + tmp = g_strdup_printf ("%s %s (%" G_GSIZE_FORMAT ")", + title, fu_wac_report_id_to_string (cmd), sz); + fu_common_dump_raw (G_LOG_DOMAIN, tmp, buf, sz); +} diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-common.h fwupd-1.2.10/plugins/wacom-usb/fu-wac-common.h --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define FU_WAC_PACKET_LEN 512 + +#define FU_WAC_REPORT_ID_COMMAND 0x01 +#define FU_WAC_REPORT_ID_STATUS 0x02 +#define FU_WAC_REPORT_ID_CONTROL 0x03 + +#define FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_MAIN 0x07 +#define FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_TOUCH 0x07 +#define FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_BLUETOOTH 0x16 + +#define FU_WAC_REPORT_ID_FW_DESCRIPTOR 0xcb /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_SWITCH_TO_FLASH_LOADER 0xcc /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_QUIT_AND_RESET 0xcd /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_READ_BLOCK_DATA 0xd1 /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_WRITE_BLOCK 0xd2 /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_ERASE_BLOCK 0xd3 /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_SET_READ_ADDRESS 0xd4 /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_GET_STATUS 0xd5 /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_UPDATE_RESET 0xd6 /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_WRITE_WORD 0xd7 /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_GET_PARAMETERS 0xd8 /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_GET_FLASH_DESCRIPTOR 0xd9 /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_GET_CHECKSUMS 0xda /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_SET_CHECKSUM_FOR_BLOCK 0xdb /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_CALCULATE_CHECKSUM_FOR_BLOCK 0xdc /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_WRITE_CHECKSUM_TABLE 0xde /* SET_FEATURE */ +#define FU_WAC_REPORT_ID_GET_CURRENT_FIRMWARE_IDX 0xe2 /* GET_FEATURE */ +#define FU_WAC_REPORT_ID_MODULE 0xe4 + +guint32 fu_wac_calculate_checksum32le (const guint8 *data, + gsize len); +guint32 fu_wac_calculate_checksum32le_bytes (GBytes *blob); +const gchar *fu_wac_report_id_to_string (guint8 report_id); +void fu_wac_buffer_dump (const gchar *title, + guint8 cmd, + const guint8 *buf, + gsize sz); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-device.c fwupd-1.2.10/plugins/wacom-usb/fu-wac-device.c --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,914 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-chunk.h" +#include "fu-wac-device.h" +#include "fu-wac-common.h" +#include "fu-wac-firmware.h" +#include "fu-wac-module-bluetooth.h" +#include "fu-wac-module-touch.h" + +#include "dfu-common.h" +#include "dfu-firmware.h" + +typedef struct __attribute__((packed)) { + guint32 start_addr; + guint32 block_sz; + guint16 write_sz; /* bit 15 is write protection flag */ +} FuWacFlashDescriptor; + +typedef enum { + FU_WAC_STATUS_UNKNOWN = 0, + FU_WAC_STATUS_WRITING = 1 << 0, + FU_WAC_STATUS_ERASING = 1 << 1, + FU_WAC_STATUS_ERROR_WRITE = 1 << 2, + FU_WAC_STATUS_ERROR_ERASE = 1 << 3, + FU_WAC_STATUS_WRITE_PROTECTED = 1 << 4, + FU_WAC_STATUS_LAST +} FuWacStatus; + +#define FU_WAC_DEVICE_TIMEOUT 5000 /* ms */ + +struct _FuWacDevice +{ + FuUsbDevice parent_instance; + GPtrArray *flash_descriptors; + GArray *checksums; + guint32 status_word; + guint16 firmware_index; + guint16 loader_ver; + guint16 read_data_sz; + guint16 write_word_sz; + guint16 write_block_sz; /* usb transfer size */ + guint16 nr_flash_blocks; + guint16 configuration; +}; + +G_DEFINE_TYPE (FuWacDevice, fu_wac_device, FU_TYPE_USB_DEVICE) + +static GString * +fu_wac_device_status_to_string (guint32 status_word) +{ + GString *str = g_string_new (NULL); + if (status_word & FU_WAC_STATUS_WRITING) + g_string_append (str, "writing,"); + if (status_word & FU_WAC_STATUS_ERASING) + g_string_append (str, "erasing,"); + if (status_word & FU_WAC_STATUS_ERROR_WRITE) + g_string_append (str, "error-write,"); + if (status_word & FU_WAC_STATUS_ERROR_ERASE) + g_string_append (str, "error-erase,"); + if (status_word & FU_WAC_STATUS_WRITE_PROTECTED) + g_string_append (str, "write-protected,"); + if (str->len == 0) { + g_string_append (str, "none"); + return str; + } + g_string_truncate (str, str->len - 1); + return str; +} + +static gboolean +fu_wav_device_flash_descriptor_is_wp (const FuWacFlashDescriptor *fd) +{ + return fd->write_sz & 0x8000; +} + +static void +fu_wac_device_to_string (FuDevice *device, GString *str) +{ + GPtrArray *children; + FuWacDevice *self = FU_WAC_DEVICE (device); + g_autoptr(GString) status_str = NULL; + + g_string_append (str, " FuWacDevice:\n"); + if (self->firmware_index != 0xffff) { + g_string_append_printf (str, " fw-index: 0x%04x\n", + self->firmware_index); + } + if (self->loader_ver > 0) { + g_string_append_printf (str, " loader-ver: 0x%04x\n", + (guint) self->loader_ver); + } + if (self->read_data_sz > 0) { + g_string_append_printf (str, " read-data-sz: 0x%04x\n", + (guint) self->read_data_sz); + } + if (self->write_word_sz > 0) { + g_string_append_printf (str, " write-word-sz: 0x%04x\n", + (guint) self->write_word_sz); + } + if (self->write_block_sz > 0) { + g_string_append_printf (str, " write-block-sz: 0x%04x\n", + (guint) self->write_block_sz); + } + if (self->nr_flash_blocks > 0) { + g_string_append_printf (str, " nr-flash-blocks: 0x%04x\n", + (guint) self->nr_flash_blocks); + } + if (self->configuration != 0xffff) { + g_string_append_printf (str, " configuration: 0x%04x\n", + (guint) self->configuration); + } + for (guint i = 0; i < self->flash_descriptors->len; i++) { + FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); + g_string_append_printf (str, " flash-descriptor-%02u:\n", i); + g_string_append_printf (str, " start-addr:\t0x%08x\n", + (guint) fd->start_addr); + g_string_append_printf (str, " block-sz:\t0x%08x\n", + (guint) fd->block_sz); + g_string_append_printf (str, " write-sz:\t0x%04x\n", + (guint) fd->write_sz & ~0x8000); + g_string_append_printf (str, " protected:\t%s\n", + fu_wav_device_flash_descriptor_is_wp (fd) ? "yes" : "no"); + } + status_str = fu_wac_device_status_to_string (self->status_word); + g_string_append_printf (str, " status:\t\t%s\n", status_str->str); + + /* print children also */ + children = fu_device_get_children (device); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + g_autofree gchar *tmp = fu_device_to_string (FU_DEVICE (child)); + g_string_append (str, " FuWacDeviceChild:\n"); + g_string_append (str, tmp); + } +} + +gboolean +fu_wac_device_get_feature_report (FuWacDevice *self, + guint8 *buf, gsize bufsz, + FuWacDeviceFeatureFlags flags, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize sz = 0; + guint8 cmd = buf[0]; + + /* hit hardware */ + if ((flags & FU_WAC_DEVICE_FEATURE_FLAG_NO_DEBUG) == 0) + fu_wac_buffer_dump ("GET", cmd, buf, bufsz); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + HID_REPORT_GET, /* bRequest */ + HID_FEATURE | cmd, /* wValue */ + 0x0000, /* wIndex */ + buf, bufsz, &sz, + FU_WAC_DEVICE_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "Failed to get feature report: "); + return FALSE; + } + if ((flags & FU_WAC_DEVICE_FEATURE_FLAG_NO_DEBUG) == 0) + fu_wac_buffer_dump ("GE2", cmd, buf, sz); + + /* check packet */ + if ((flags & FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC) == 0 && sz != bufsz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "packet get bytes %" G_GSIZE_FORMAT + " expected %" G_GSIZE_FORMAT, + sz, bufsz); + return FALSE; + } + if (buf[0] != cmd) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "command response was %i expected %i", + buf[0], cmd); + return FALSE; + } + return TRUE; +} + +gboolean +fu_wac_device_set_feature_report (FuWacDevice *self, + guint8 *buf, gsize bufsz, + FuWacDeviceFeatureFlags flags, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize sz = 0; + guint8 cmd = buf[0]; + + /* hit hardware */ + fu_wac_buffer_dump ("SET", cmd, buf, bufsz); + if (g_getenv ("FWUPD_WAC_EMULATE") != NULL) + return TRUE; + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + HID_REPORT_SET, /* bRequest */ + HID_FEATURE | cmd, /* wValue */ + 0x0000, /* wIndex */ + buf, bufsz, &sz, + FU_WAC_DEVICE_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "Failed to set feature report: "); + return FALSE; + } + + /* check packet */ + if ((flags & FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC) == 0 && sz != bufsz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "packet sent bytes %" G_GSIZE_FORMAT + " expected %" G_GSIZE_FORMAT, + sz, bufsz); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wac_device_ensure_flash_descriptors (FuWacDevice *self, GError **error) +{ + gsize sz = (self->nr_flash_blocks * 10) + 1; + g_autofree guint8 *buf = NULL; + + /* already done */ + if (self->flash_descriptors->len > 0) + return TRUE; + + /* hit hardware */ + buf = g_malloc (sz); + memset (buf, 0xff, sz); + buf[0] = FU_WAC_REPORT_ID_GET_FLASH_DESCRIPTOR; + if (!fu_wac_device_get_feature_report (self, buf, sz, + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + for (guint i = 0; i < self->nr_flash_blocks; i++) { + FuWacFlashDescriptor *fd = g_new0 (FuWacFlashDescriptor, 1); + const guint blksz = sizeof(FuWacFlashDescriptor); + fd->start_addr = fu_common_read_uint32 (buf + (i * blksz) + 1, G_LITTLE_ENDIAN); + fd->block_sz = fu_common_read_uint32 (buf + (i * blksz) + 5, G_LITTLE_ENDIAN); + fd->write_sz = fu_common_read_uint16 (buf + (i * blksz) + 9, G_LITTLE_ENDIAN); + g_ptr_array_add (self->flash_descriptors, fd); + } + g_debug ("added %u flash descriptors", self->flash_descriptors->len); + return TRUE; +} + +static gboolean +fu_wac_device_ensure_status (FuWacDevice *self, GError **error) +{ + g_autoptr(GString) str = NULL; + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_STATUS, + [1 ... 4] = 0xff }; + + /* hit hardware */ + buf[0] = FU_WAC_REPORT_ID_GET_STATUS; + if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + self->status_word = fu_common_read_uint32 (buf + 1, G_LITTLE_ENDIAN); + str = fu_wac_device_status_to_string (self->status_word); + g_debug ("status now: %s", str->str); + return TRUE; +} + +static gboolean +fu_wac_device_ensure_checksums (FuWacDevice *self, GError **error) +{ + gsize sz = (self->nr_flash_blocks * 4) + 5; + guint32 updater_version; + g_autofree guint8 *buf = g_malloc (sz); + + /* hit hardware */ + memset (buf, 0xff, sz); + buf[0] = FU_WAC_REPORT_ID_GET_CHECKSUMS; + if (!fu_wac_device_get_feature_report (self, buf, sz, + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + updater_version = fu_common_read_uint32 (buf + 1, G_LITTLE_ENDIAN); + g_debug ("updater-version: %" G_GUINT32_FORMAT, updater_version); + + /* get block checksums */ + g_array_set_size (self->checksums, 0); + for (guint i = 0; i < self->nr_flash_blocks; i++) { + guint32 csum = fu_common_read_uint32 (buf + 5 + (i * 4), G_LITTLE_ENDIAN); + g_debug ("checksum block %02u: 0x%08x", i, (guint) csum); + g_array_append_val (self->checksums, csum); + } + g_debug ("added %u checksums", self->flash_descriptors->len); + + return TRUE; +} + + +static gboolean +fu_wac_device_ensure_firmware_index (FuWacDevice *self, GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_CURRENT_FIRMWARE_IDX, + [1 ... 2] = 0xff }; + + /* hit hardware */ + if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + self->firmware_index = fu_common_read_uint16 (buf + 1, G_LITTLE_ENDIAN); + return TRUE; +} + +static gboolean +fu_wac_device_ensure_parameters (FuWacDevice *self, GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_PARAMETERS, + [1 ... 12] = 0xff }; + + /* hit hardware */ + if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + self->loader_ver = fu_common_read_uint16 (buf + 1, G_LITTLE_ENDIAN); + self->read_data_sz = fu_common_read_uint16 (buf + 3, G_LITTLE_ENDIAN); + self->write_word_sz = fu_common_read_uint16 (buf + 5, G_LITTLE_ENDIAN); + self->write_block_sz = fu_common_read_uint16 (buf + 7, G_LITTLE_ENDIAN); + self->nr_flash_blocks = fu_common_read_uint16 (buf + 9, G_LITTLE_ENDIAN); + self->configuration = fu_common_read_uint16 (buf + 11, G_LITTLE_ENDIAN); + return TRUE; +} + +static gboolean +fu_wac_device_write_block (FuWacDevice *self, + guint32 addr, + GBytes *blob, + GError **error) +{ + const guint8 *tmp; + gsize bufsz = self->write_block_sz + 5; + gsize sz = 0; + g_autofree guint8 *buf = NULL; + + /* check size */ + tmp = g_bytes_get_data (blob, &sz); + if (sz > self->write_block_sz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "packet was too large at %" G_GSIZE_FORMAT " bytes", + sz); + return FALSE; + } + + /* build packet */ + buf = g_malloc (bufsz); + memset (buf, 0xff, bufsz); + buf[0] = FU_WAC_REPORT_ID_WRITE_BLOCK; + fu_common_write_uint32 (buf + 1, addr, G_LITTLE_ENDIAN); + if (sz > 0) + memcpy (buf + 5, tmp, sz); + + /* hit hardware */ + return fu_wac_device_set_feature_report (self, buf, bufsz, + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error); +} + +static gboolean +fu_wac_device_erase_block (FuWacDevice *self, guint32 addr, GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_ERASE_BLOCK, + [1 ... 4] = 0xff }; + + /* build packet */ + fu_common_write_uint32 (buf + 1, addr, G_LITTLE_ENDIAN); + + /* hit hardware */ + return fu_wac_device_set_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error); +} + +gboolean +fu_wac_device_update_reset (FuWacDevice *self, GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_UPDATE_RESET, + [1 ... 4] = 0xff }; + + /* hit hardware */ + return fu_wac_device_set_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error); +} + +static gboolean +fu_wac_device_set_checksum_of_block (FuWacDevice *self, + guint16 block_nr, + guint32 checksum, + GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_SET_CHECKSUM_FOR_BLOCK, + [1 ... 6] = 0xff }; + + /* build packet */ + fu_common_write_uint16 (buf + 1, block_nr, G_LITTLE_ENDIAN); + fu_common_write_uint32 (buf + 3, checksum, G_LITTLE_ENDIAN); + + /* hit hardware */ + return fu_wac_device_set_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error); +} + +static gboolean +fu_wac_device_calculate_checksum_of_block (FuWacDevice *self, + guint16 block_nr, + GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_CALCULATE_CHECKSUM_FOR_BLOCK, + [1 ... 2] = 0xff }; + + /* build packet */ + fu_common_write_uint16 (buf + 1, block_nr, G_LITTLE_ENDIAN); + + /* hit hardware */ + return fu_wac_device_set_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error); +} + +static gboolean +fu_wac_device_write_checksum_table (FuWacDevice *self, GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_WRITE_CHECKSUM_TABLE, + [1 ... 4] = 0xff }; + + /* hit hardware */ + return fu_wac_device_set_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error); +} + +static gboolean +fu_wac_device_switch_to_flash_loader (FuWacDevice *self, GError **error) +{ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_SWITCH_TO_FLASH_LOADER, + [1] = 0x05, + [2] = 0x6a }; + + /* hit hardware */ + return fu_wac_device_set_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error); +} + +static gboolean +fu_wac_device_write_firmware (FuDevice *device, + GBytes *blob, + FwupdInstallFlags flags, + GError **error) +{ + DfuElement *element; + DfuImage *image; + FuWacDevice *self = FU_WAC_DEVICE (device); + gsize blocks_done = 0; + gsize blocks_total = 0; + g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(GHashTable) fd_blobs = NULL; + g_autofree guint32 *csum_local = NULL; + + /* load .wac file, including metadata */ + if (!fu_wac_firmware_parse_data (firmware, blob, + DFU_FIRMWARE_PARSE_FLAG_NONE, + error)) + return FALSE; + if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_SREC) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "expected firmware format is 'srec', got '%s'", + dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); + return FALSE; + } + + /* enter flash mode */ + if (!fu_wac_device_switch_to_flash_loader (self, error)) + return FALSE; + + /* get current selected device */ + if (!fu_wac_device_ensure_firmware_index (self, error)) + return FALSE; + + /* use the correct image from the firmware */ + image = dfu_firmware_get_image (firmware, self->firmware_index == 1 ? 1 : 0); + if (image == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no firmware image for index %" G_GUINT16_FORMAT, + self->firmware_index); + return FALSE; + } + element = dfu_image_get_element_default (image); + if (element == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no element in image %" G_GUINT16_FORMAT, + self->firmware_index); + return FALSE; + } + g_debug ("using element at addr 0x%0x", + (guint) dfu_element_get_address (element)); + + /* get firmware parameters (page sz and transfer sz) */ + if (!fu_wac_device_ensure_parameters (self, error)) + return FALSE; + + /* get the current flash descriptors */ + if (!fu_wac_device_ensure_flash_descriptors (self, error)) + return FALSE; + + /* get the updater protocol version */ + if (!fu_wac_device_ensure_checksums (self, error)) + return FALSE; + + /* clear all checksums of pages */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + for (guint16 i = 0; i < self->flash_descriptors->len; i++) { + FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); + if (fu_wav_device_flash_descriptor_is_wp (fd)) + continue; + if (!fu_wac_device_set_checksum_of_block (self, i, 0x0, error)) + return FALSE; + } + + /* get the blobs for each chunk */ + fd_blobs = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) g_bytes_unref); + for (guint16 i = 0; i < self->flash_descriptors->len; i++) { + FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); + GBytes *blob_block; + g_autoptr(GBytes) blob_tmp = NULL; + + if (fu_wav_device_flash_descriptor_is_wp (fd)) + continue; + blob_tmp = dfu_element_get_contents_chunk (element, + fd->start_addr, + fd->block_sz, + NULL); + if (blob_tmp == NULL) + break; + blob_block = dfu_utils_bytes_pad (blob_tmp, fd->block_sz); + g_hash_table_insert (fd_blobs, fd, blob_block); + } + + /* checksum actions post-write */ + blocks_total = g_hash_table_size (fd_blobs) + 2; + + /* write the data into the flash page */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + csum_local = g_new0 (guint32, self->flash_descriptors->len); + for (guint16 i = 0; i < self->flash_descriptors->len; i++) { + FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); + GBytes *blob_block; + g_autoptr(GPtrArray) chunks = NULL; + + /* if page is protected */ + if (fu_wav_device_flash_descriptor_is_wp (fd)) + continue; + + /* get data for page */ + blob_block = g_hash_table_lookup (fd_blobs, fd); + if (blob_block == NULL) + break; + + /* ignore empty blocks */ + if (fu_common_bytes_is_empty (blob_block)) { + g_debug ("empty block, ignoring"); + fu_device_set_progress_full (device, blocks_done++, blocks_total); + continue; + } + + /* erase entire block */ + if (!fu_wac_device_erase_block (self, i, error)) + return FALSE; + + /* write block in chunks */ + chunks = fu_chunk_array_new_from_bytes (blob_block, + fd->start_addr, + 0, /* page_sz */ + self->write_block_sz); + for (guint j = 0; j < chunks->len; j++) { + FuChunk *chk = g_ptr_array_index (chunks, j); + g_autoptr(GBytes) blob_chunk = g_bytes_new (chk->data, chk->data_sz); + if (!fu_wac_device_write_block (self, chk->address, blob_chunk, error)) + return FALSE; + } + + /* calculate expected checksum and save to device RAM */ + csum_local[i] = fu_wac_calculate_checksum32le_bytes (blob_block); + g_debug ("block checksum %02u: 0x%08x", i, csum_local[i]); + if (!fu_wac_device_set_checksum_of_block (self, i, csum_local[i], error)) + return FALSE; + + /* update device progress */ + fu_device_set_progress_full (device, blocks_done++, blocks_total); + } + + /* calculate CRC inside device */ + for (guint16 i = 0; i < self->flash_descriptors->len; i++) { + if (!fu_wac_device_calculate_checksum_of_block (self, i, error)) + return FALSE; + } + + /* update device progress */ + fu_device_set_progress_full (device, blocks_done++, blocks_total); + + /* read all CRC of all pages and verify with local CRC */ + if (!fu_wac_device_ensure_checksums (self, error)) + return FALSE; + for (guint16 i = 0; i < self->flash_descriptors->len; i++) { + FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); + GBytes *blob_block; + guint32 csum_rom; + + /* if page is protected */ + if (fu_wav_device_flash_descriptor_is_wp (fd)) + continue; + + /* no more written pages */ + blob_block = g_hash_table_lookup (fd_blobs, fd); + if (blob_block == NULL) + continue; + if (fu_common_bytes_is_empty (blob_block)) + continue; + + /* check checksum matches */ + csum_rom = g_array_index (self->checksums, guint32, i); + if (csum_rom != csum_local[i]) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed local checksum at block %u, " + "got 0x%08x expected 0x%08x", i, + (guint) csum_rom, (guint) csum_local[i]); + return FALSE; + } + g_debug ("matched checksum at block %u of 0x%08x", i, csum_rom); + } + + /* update device progress */ + fu_device_set_progress_full (device, blocks_done++, blocks_total); + + /* store host CRC into flash */ + if (!fu_wac_device_write_checksum_table (self, error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, blocks_total, blocks_total); + return TRUE; +} + +static gboolean +fu_wac_device_add_modules_bluetooth (FuWacDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + g_autofree gchar *name = NULL; + g_autofree gchar *version = NULL; + g_autoptr(FuWacModule) module = NULL; + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_BLUETOOTH, + [1 ... 14] = 0xff }; + + buf[0] = FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_BLUETOOTH; + if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error)) { + g_prefix_error (error, "Failed to get GetFirmwareVersionBluetooth: "); + return FALSE; + } + + /* success */ + name = g_strdup_printf ("%s [Legacy Bluetooth Module]", + fu_device_get_name (FU_DEVICE (self))); + version = g_strdup_printf ("%x.%x", (guint) buf[2], (guint) buf[1]); + module = fu_wac_module_bluetooth_new (usb_device); + fu_device_add_child (FU_DEVICE (self), FU_DEVICE (module)); + fu_device_set_name (FU_DEVICE (module), name); + fu_device_set_version (FU_DEVICE (module), version, FWUPD_VERSION_FORMAT_PAIR); + return TRUE; +} + +static gboolean +fu_wac_device_add_modules_legacy (FuWacDevice *self, GError **error) +{ + g_autoptr(GError) error_bt = NULL; + + /* optional bluetooth */ + if (!fu_wac_device_add_modules_bluetooth (self, &error_bt)) + g_debug ("no bluetooth hardware: %s", error_bt->message); + + return TRUE; +} + +static gboolean +fu_wac_device_add_modules (FuWacDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + g_autofree gchar *version_bootloader = NULL; + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_FW_DESCRIPTOR, + [1 ... 31] = 0xff }; + + if (!fu_wac_device_get_feature_report (self, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_NONE, + error)) { + g_prefix_error (error, "Failed to get DeviceFirmwareDescriptor: "); + return FALSE; + } + + /* verify bootloader is compatible */ + if (buf[1] != 0x01) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "bootloader major version not compatible"); + return FALSE; + } + + /* verify the number of submodules is possible */ + if (buf[3] > (512 - 4) / 4) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "number of submodules is impossible"); + return FALSE; + } + + /* bootloader version */ + version_bootloader = g_strdup_printf ("%u.%u", buf[1], buf[2]); + fu_device_set_version_bootloader (FU_DEVICE (self), version_bootloader); + + /* get versions of each submodule */ + for (guint8 i = 0; i < buf[3]; i++) { + guint8 fw_type = buf[(i * 4) + 4] & ~0x80; + g_autofree gchar *name = NULL; + g_autofree gchar *version = NULL; + g_autoptr(FuWacModule) module = NULL; + + /* version number is decimal */ + version = g_strdup_printf ("%u.%u", buf[(i * 4) + 5], buf[(i * 4) + 6]); + + switch (fw_type) { + case FU_WAC_MODULE_FW_TYPE_TOUCH: + module = fu_wac_module_touch_new (usb_device); + name = g_strdup_printf ("%s [Touch Module]", + fu_device_get_name (FU_DEVICE (self))); + fu_device_add_child (FU_DEVICE (self), FU_DEVICE (module)); + fu_device_set_name (FU_DEVICE (module), name); + fu_device_set_version (FU_DEVICE (module), version, FWUPD_VERSION_FORMAT_PAIR); + break; + case FU_WAC_MODULE_FW_TYPE_BLUETOOTH: + module = fu_wac_module_bluetooth_new (usb_device); + name = g_strdup_printf ("%s [Bluetooth Module]", + fu_device_get_name (FU_DEVICE (self))); + fu_device_add_child (FU_DEVICE (self), FU_DEVICE (module)); + fu_device_set_name (FU_DEVICE (module), name); + fu_device_set_version (FU_DEVICE (module), version, FWUPD_VERSION_FORMAT_PAIR); + break; + case FU_WAC_MODULE_FW_TYPE_MAIN: + fu_device_set_version (FU_DEVICE (self), version, FWUPD_VERSION_FORMAT_PAIR); + break; + default: + g_warning ("unknown submodule type 0x%0x", fw_type); + break; + } + } + return TRUE; +} + +static gboolean +fu_wac_device_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* open device */ + if (!g_usb_device_claim_interface (usb_device, 0x00, /* HID */ + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to claim HID interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wac_device_setup (FuDevice *device, GError **error) +{ + FuWacDevice *self = FU_WAC_DEVICE (device); + + /* get current status */ + if (!fu_wac_device_ensure_status (self, error)) + return FALSE; + + /* get version of each sub-module */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION)) { + if (!fu_wac_device_add_modules_legacy (self, error)) + return FALSE; + } else { + if (!fu_wac_device_add_modules (self, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wac_device_close (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* reattach wacom.ko */ + if (!g_usb_device_release_interface (usb_device, 0x00, /* HID */ + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to re-attach interface: "); + return FALSE; + } + + /* The hidcore subsystem uses a generic power_supply that has a deferred + * work item that will lock the device. When removing the power_supply, + * we take the lock, then cancel the work item which needs to take the + * lock too. This needs to be fixed in the kernel, but for the moment + * this should let the kernel unstick itself. */ + g_usleep (20 * 1000); + + /* success */ + return TRUE; +} + +static void +fu_wac_device_init (FuWacDevice *self) +{ + self->flash_descriptors = g_ptr_array_new_with_free_func (g_free); + self->checksums = g_array_new (FALSE, FALSE, sizeof(guint32)); + self->configuration = 0xffff; + self->firmware_index = 0xffff; + fu_device_add_icon (FU_DEVICE (self), "input-tablet"); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_install_duration (FU_DEVICE (self), 10); +} + +static void +fu_wac_device_finalize (GObject *object) +{ + FuWacDevice *self = FU_WAC_DEVICE (object); + + g_ptr_array_unref (self->flash_descriptors); + g_array_unref (self->checksums); + + G_OBJECT_CLASS (fu_wac_device_parent_class)->finalize (object); +} + +static void +fu_wac_device_class_init (FuWacDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + object_class->finalize = fu_wac_device_finalize; + klass_device->write_firmware = fu_wac_device_write_firmware; + klass_device->to_string = fu_wac_device_to_string; + klass_device->setup = fu_wac_device_setup; + klass_usb_device->open = fu_wac_device_open; + klass_usb_device->close = fu_wac_device_close; +} + +FuWacDevice * +fu_wac_device_new (FuUsbDevice *device) +{ + FuWacDevice *self = g_object_new (FU_TYPE_WAC_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-device.h fwupd-1.2.10/plugins/wacom-usb/fu-wac-device.h --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WAC_DEVICE (fu_wac_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuWacDevice, fu_wac_device, FU, WAC_DEVICE, FuUsbDevice) + +typedef enum { + FU_WAC_DEVICE_FEATURE_FLAG_NONE = 0, + FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC = 1 << 0, + FU_WAC_DEVICE_FEATURE_FLAG_NO_DEBUG = 1 << 1, + FU_WAC_DEVICE_FEATURE_FLAG_LAST +} FuWacDeviceFeatureFlags; + +FuWacDevice *fu_wac_device_new (FuUsbDevice *device); +gboolean fu_wac_device_update_reset (FuWacDevice *self, + GError **error); +gboolean fu_wac_device_get_feature_report (FuWacDevice *self, + guint8 *buf, + gsize bufsz, + FuWacDeviceFeatureFlags flags, + GError **error); +gboolean fu_wac_device_set_feature_report (FuWacDevice *self, + guint8 *buf, + gsize bufsz, + FuWacDeviceFeatureFlags flags, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-firmware.c fwupd-1.2.10/plugins/wacom-usb/fu-wac-firmware.c --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-firmware.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "dfu-element.h" +#include "dfu-format-srec.h" +#include "dfu-image.h" + +#include "fu-wac-firmware.h" + +#include "fwupd-error.h" + +typedef struct { + guint32 addr; + guint32 sz; + guint32 prog_start_addr; +} DfuFirmwareWacHeaderRecord; + +/** + * fu_wac_firmware_parse_data: + * @firmware: a #DfuFirmware + * @bytes: data to parse + * @flags: some #DfuFirmwareParseFlags + * @error: a #GError, or %NULL + * + * Unpacks into a firmware object from DfuSe data. + * + * Returns: %TRUE for success + **/ +gboolean +fu_wac_firmware_parse_data (DfuFirmware *firmware, + GBytes *bytes, + DfuFirmwareParseFlags flags, + GError **error) +{ + gsize len; + guint8 *data; + g_auto(GStrv) lines = NULL; + g_autoptr(GString) image_buffer = NULL; + g_autofree gchar *data_str = NULL; + guint8 images_cnt = 0; + g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func (g_free); + + /* check the prefix (BE) */ + data = (guint8 *) g_bytes_get_data (bytes, &len); + if (memcmp (data, "WACOM", 5) != 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid .wac prefix"); + return FALSE; + } + + /* parse each line */ + data_str = g_strndup ((const gchar *) data, len); + lines = g_strsplit (data_str, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + g_autofree gchar *cmd = g_strndup (lines[i], 2); + + /* remove windows line endings */ + g_strdelimit (lines[i], "\r", '\0'); + + /* Wacom-specific metadata */ + if (g_strcmp0 (cmd, "WA") == 0) { + guint cmdlen = strlen (lines[i]); + + /* header info record */ + if (memcmp (lines[i] + 2, "COM", 3) == 0) { + guint8 header_image_cnt = 0; + if (cmdlen != 40) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid header, got %u bytes", + cmdlen); + return FALSE; + } + header_image_cnt = dfu_utils_buffer_parse_uint4 (lines[i] + 5); + for (guint j = 0; j < header_image_cnt; j++) { + DfuFirmwareWacHeaderRecord *hdr = g_new0 (DfuFirmwareWacHeaderRecord, 1); + hdr->addr = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 6); + hdr->sz = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 14); + g_ptr_array_add (header_infos, hdr); + g_debug ("header_fw%u_addr: 0x%x", j, hdr->addr); + g_debug ("header_fw%u_sz: 0x%x", j, hdr->sz); + } + continue; + } + + /* firmware headline record */ + if (cmdlen == 13) { + DfuFirmwareWacHeaderRecord *hdr; + guint8 idx = dfu_utils_buffer_parse_uint4 (lines[i] + 2); + if (idx == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "headline %u invalid", + idx); + return FALSE; + } + if (idx > header_infos->len) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "headline %u exceeds header count %u", + idx, header_infos->len); + return FALSE; + } + hdr = g_ptr_array_index (header_infos, idx - 1); + hdr->prog_start_addr = dfu_utils_buffer_parse_uint32 (lines[i] + 3); + if (hdr->prog_start_addr != hdr->addr) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "programming address 0x%x != " + "base address 0x%0x for idx %u", + hdr->prog_start_addr, + hdr->addr, + idx); + return FALSE; + } + g_debug ("programing-start-address: 0x%x", hdr->prog_start_addr); + continue; + } + + g_debug ("unknown Wacom-specific metadata"); + continue; + } + + /* start */ + if (g_strcmp0 (cmd, "S0") == 0) { + if (image_buffer != NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "duplicate S0 without S7"); + return FALSE; + } + image_buffer = g_string_new (NULL); + } + + /* these are things we want to include in the image */ + if (g_strcmp0 (cmd, "S0") == 0 || + g_strcmp0 (cmd, "S1") == 0 || + g_strcmp0 (cmd, "S2") == 0 || + g_strcmp0 (cmd, "S3") == 0 || + g_strcmp0 (cmd, "S5") == 0 || + g_strcmp0 (cmd, "S7") == 0 || + g_strcmp0 (cmd, "S8") == 0 || + g_strcmp0 (cmd, "S9") == 0) { + if (image_buffer == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s without S0", cmd); + return FALSE; + } + g_string_append_printf (image_buffer, "%s\n", lines[i]); + } + + /* end */ + if (g_strcmp0 (cmd, "S7") == 0) { + g_autoptr(GBytes) blob = NULL; + g_autoptr(DfuImage) image = dfu_image_new (); + DfuFirmwareWacHeaderRecord *hdr; + + /* get the correct relocated start address */ + if (images_cnt >= header_infos->len) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s without header", cmd); + return FALSE; + } + hdr = g_ptr_array_index (header_infos, images_cnt); + + if (image_buffer == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s with missing image buffer", cmd); + return FALSE; + } + + /* parse SREC file and add as image */ + blob = g_bytes_new (image_buffer->str, image_buffer->len); + if (!dfu_image_from_srec (image, blob, hdr->addr, flags, error)) + return FALSE; + + /* the alt-setting is used for the firmware index */ + dfu_image_set_alt_setting (image, images_cnt); + dfu_firmware_add_image (firmware, image); + images_cnt++; + + /* clear the image buffer */ + g_string_free (image_buffer, TRUE); + image_buffer = NULL; + } + } + + /* verify data is complete */ + if (image_buffer != NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "truncated data: no S7"); + return FALSE; + } + + /* ensure this matched the header */ + if (header_infos->len != images_cnt) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "not enough images %u for header count %u", + images_cnt, + header_infos->len); + return FALSE; + } + + /* success */ + dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_SREC); + return TRUE; +} diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-firmware.h fwupd-1.2.10/plugins/wacom-usb/fu-wac-firmware.h --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-firmware.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-firmware.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "dfu-firmware.h" + +G_BEGIN_DECLS + +gboolean fu_wac_firmware_parse_data (DfuFirmware *firmware, + GBytes *bytes, + DfuFirmwareParseFlags flags, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-bluetooth.c fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-bluetooth.c --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-bluetooth.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-bluetooth.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wac-common.h" +#include "fu-wac-device.h" +#include "fu-wac-module-bluetooth.h" + +struct _FuWacModuleBluetooth +{ + FuWacModule parent_instance; +}; + +G_DEFINE_TYPE (FuWacModuleBluetooth, fu_wac_module_bluetooth, FU_TYPE_WAC_MODULE) + +#define FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ 256 +#define FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_START 0x3000 +#define FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_STOP 0x8000 + +typedef struct { + guint8 preamble[7]; + guint8 addr[3]; + guint8 crc; + guint8 cdata[FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ]; +} FuWacModuleBluetoothBlockData; + +static void +fu_wac_module_bluetooth_calculate_crc_byte (guint8 *crc, guint8 data) +{ + guint8 c[8]; + guint8 m[8]; + guint8 r[8]; + + /* find out what bits are set */ + for (guint i = 0; i < 8; i++) { + c[i] = (*crc & (1 << i)) != 0; + m[i] = (data & (1 << i)) != 0; + } + + /* do CRC on byte */ + r[7] = (c[7] ^ m[4] ^ c[3] ^ m[3] ^ c[4] ^ m[6] ^ c[1] ^ m[0]); + r[6] = (c[6] ^ m[5] ^ c[2] ^ m[4] ^ c[3] ^ m[7] ^ c[0] ^ m[1]); + r[5] = (c[5] ^ m[6] ^ c[1] ^ m[5] ^ c[2] ^ m[2]); + r[4] = (c[4] ^ m[7] ^ c[0] ^ m[6] ^ c[1] ^ m[3]); + r[3] = (m[7] ^ m[0] ^ c[7] ^ c[0] ^ m[3] ^ c[4] ^ m[6] ^ c[1]); + r[2] = (m[1] ^ c[6] ^ m[0] ^ c[7] ^ m[3] ^ c[4] ^ m[7] ^ c[0] ^ m[6] ^ c[1]); + r[1] = (m[2] ^ c[5] ^ m[1] ^ c[6] ^ m[4] ^ c[3] ^ m[7] ^ c[0]); + r[0] = (m[3] ^ c[4] ^ m[2] ^ c[5] ^ m[5] ^ c[2]); + + /* copy back into CRC */ + *crc = 0; + for (guint i = 0; i < 8; i++) { + if (r[i] == 0) + continue; + *crc |= (1 << i); + } +} + +static guint8 +fu_wac_module_bluetooth_calculate_crc (const guint8 *data, gsize sz) +{ + guint8 crc = 0; + for (gsize i = 0; i < sz; i++) + fu_wac_module_bluetooth_calculate_crc_byte (&crc, data[i]); + return crc; +} + +static GPtrArray * +fu_wac_module_bluetooth_parse_blocks (const guint8 *data, gsize sz, gboolean skip_user_data) +{ + const guint8 preamble[] = {0x02, 0x00, 0x0f, 0x06, 0x01, 0x08, 0x01}; + GPtrArray *blocks = g_ptr_array_new_with_free_func (g_free); + for (guint addr = 0x0; addr < sz; addr += FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ) { + FuWacModuleBluetoothBlockData *bd; + gsize cdata_sz = FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ; + + /* user data area */ + if (skip_user_data && + addr >= FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_START && + addr < FU_WAC_MODULE_BLUETOOTH_ADDR_USERDATA_STOP) + continue; + + bd = g_new0 (FuWacModuleBluetoothBlockData, 1); + memcpy (bd->preamble, preamble, sizeof (preamble)); + bd->addr[0] = (addr >> 16) & 0xff; + bd->addr[1] = (addr >> 8) & 0xff; + bd->addr[2] = addr & 0xff; + memset (bd->cdata, 0xff, FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ); + + /* if file is not in multiples of payload size */ + if (addr + FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ >= sz) + cdata_sz = sz - addr; + memcpy (bd->cdata, data + addr, cdata_sz); + bd->crc = fu_wac_module_bluetooth_calculate_crc (bd->cdata, + FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ); + g_ptr_array_add (blocks, bd); + } + return blocks; +} + +static gboolean +fu_wac_module_bluetooth_write_firmware (FuDevice *device, + GBytes *blob, + FwupdInstallFlags flags, + GError **error) +{ + FuWacModule *self = FU_WAC_MODULE (device); + const guint8 *data; + gsize len = 0; + gsize blocks_total = 0; + const guint8 buf_start[] = { 0x00 }; + g_autoptr(GPtrArray) blocks = NULL; + g_autoptr(GBytes) blob_start = g_bytes_new_static (buf_start, 1); + + /* build each data packet */ + data = g_bytes_get_data (blob, &len); + blocks = fu_wac_module_bluetooth_parse_blocks (data, len, TRUE); + blocks_total = blocks->len + 2; + + /* start, which will erase the module */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_START, blob_start, error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, 1, blocks_total); + + /* data */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < blocks->len; i++) { + FuWacModuleBluetoothBlockData *bd = g_ptr_array_index (blocks, i); + guint8 buf[256+11]; + g_autoptr(GBytes) blob_chunk = NULL; + + /* build data packet */ + memset (buf, 0xff, sizeof(buf)); + memcpy(&buf[0], bd->preamble, 7); + memcpy(&buf[7], bd->addr, 3); + buf[10] = bd->crc; + memcpy (&buf[11], bd->cdata, sizeof(bd->cdata)); + blob_chunk = g_bytes_new (buf, sizeof(buf)); + if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_DATA, + blob_chunk, error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, i + 1, blocks_total); + } + + /* end */ + if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_END, NULL, error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, blocks_total, blocks_total); + return TRUE; +} + +static void +fu_wac_module_bluetooth_init (FuWacModuleBluetooth *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_install_duration (FU_DEVICE (self), 30); +} + +static void +fu_wac_module_bluetooth_class_init (FuWacModuleBluetoothClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->write_firmware = fu_wac_module_bluetooth_write_firmware; +} + +FuWacModule * +fu_wac_module_bluetooth_new (GUsbDevice *usb_device) +{ + FuWacModule *module = NULL; + module = g_object_new (FU_TYPE_WAC_MODULE_BLUETOOTH, + "usb-device", usb_device, + "fw-type", FU_WAC_MODULE_FW_TYPE_BLUETOOTH, + NULL); + return module; +} diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-bluetooth.h fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-bluetooth.h --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-bluetooth.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-bluetooth.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-wac-module.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WAC_MODULE_BLUETOOTH (fu_wac_module_bluetooth_get_type ()) +G_DECLARE_FINAL_TYPE (FuWacModuleBluetooth, fu_wac_module_bluetooth, FU, WAC_MODULE_BLUETOOTH, FuWacModule) + +FuWacModule *fu_wac_module_bluetooth_new (GUsbDevice *usb_device); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-module.c fwupd-1.2.10/plugins/wacom-usb/fu-wac-module.c --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-module.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-module.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wac-module.h" +#include "fu-wac-common.h" +#include "fu-wac-device.h" + +#include "dfu-common.h" +#include "dfu-firmware.h" + +#define FU_WAC_MODULE_STATUS_OK 0 +#define FU_WAC_MODULE_STATUS_BUSY 1 +#define FU_WAC_MODULE_STATUS_ERR_CRC 2 +#define FU_WAC_MODULE_STATUS_ERR_CMD 3 +#define FU_WAC_MODULE_STATUS_ERR_HW_ACCESS_FAIL 4 +#define FU_WAC_MODULE_STATUS_ERR_FLASH_NO_SUPPORT 5 +#define FU_WAC_MODULE_STATUS_ERR_MODE_WRONG 6 +#define FU_WAC_MODULE_STATUS_ERR_MPU_NO_SUPPORT 7 +#define FU_WAC_MODULE_STATUS_ERR_VERSION_NO_SUPPORT 8 +#define FU_WAC_MODULE_STATUS_ERR_ERASE 9 +#define FU_WAC_MODULE_STATUS_ERR_WRITE 10 +#define FU_WAC_MODULE_STATUS_ERR_EXIT 11 +#define FU_WAC_MODULE_STATUS_ERR 12 +#define FU_WAC_MODULE_STATUS_ERR_INVALID_OP 13 +#define FU_WAC_MODULE_STATUS_ERR_WRONG_IMAGE 14 + +typedef struct { + GUsbDevice *usb_device; + guint8 fw_type; + guint8 command; + guint8 status; +} FuWacModulePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuWacModule, fu_wac_module, FU_TYPE_DEVICE) +#define GET_PRIVATE(o) (fu_wac_module_get_instance_private (o)) + +enum { + PROP_0, + PROP_FW_TYPE, + PROP_USB_DEVICE, + PROP_LAST +}; + +static const gchar * +fu_wac_module_fw_type_to_string (guint8 fw_type) +{ + if (fw_type == FU_WAC_MODULE_FW_TYPE_TOUCH) + return "touch"; + if (fw_type == FU_WAC_MODULE_FW_TYPE_BLUETOOTH) + return "bluetooth"; + if (fw_type == FU_WAC_MODULE_FW_TYPE_EMR_CORRECTION) + return "emr-correction"; + if (fw_type == FU_WAC_MODULE_FW_TYPE_BLUETOOTH_HID) + return "bluetooth-hid"; + return NULL; +} + +static const gchar * +fu_wac_module_command_to_string (guint8 command) +{ + if (command == FU_WAC_MODULE_COMMAND_START) + return "start"; + if (command == FU_WAC_MODULE_COMMAND_DATA) + return "data"; + if (command == FU_WAC_MODULE_COMMAND_END) + return "end"; + return NULL; +} + +static const gchar * +fu_wac_module_status_to_string (guint8 status) +{ + if (status == FU_WAC_MODULE_STATUS_OK) + return "ok"; + if (status == FU_WAC_MODULE_STATUS_BUSY) + return "busy"; + if (status == FU_WAC_MODULE_STATUS_ERR_CRC) + return "err-crc"; + if (status == FU_WAC_MODULE_STATUS_ERR_CMD) + return "err-cmd"; + if (status == FU_WAC_MODULE_STATUS_ERR_HW_ACCESS_FAIL) + return "err-hw-access-fail"; + if (status == FU_WAC_MODULE_STATUS_ERR_FLASH_NO_SUPPORT) + return "err-flash-no-support"; + if (status == FU_WAC_MODULE_STATUS_ERR_MODE_WRONG) + return "err-mode-wrong"; + if (status == FU_WAC_MODULE_STATUS_ERR_MPU_NO_SUPPORT) + return "err-mpu-no-support"; + if (status == FU_WAC_MODULE_STATUS_ERR_VERSION_NO_SUPPORT) + return "erro-version-no-support"; + if (status == FU_WAC_MODULE_STATUS_ERR_ERASE) + return "err-erase"; + if (status == FU_WAC_MODULE_STATUS_ERR_WRITE) + return "err-write"; + if (status == FU_WAC_MODULE_STATUS_ERR_EXIT) + return "err-exit"; + if (status == FU_WAC_MODULE_STATUS_ERR) + return "err-err"; + if (status == FU_WAC_MODULE_STATUS_ERR_INVALID_OP) + return "err-invalid-op"; + if (status == FU_WAC_MODULE_STATUS_ERR_WRONG_IMAGE) + return "err-wrong-image"; + return NULL; +} + +static void +fu_wac_module_to_string (FuDevice *device, GString *str) +{ + FuWacModule *self = FU_WAC_MODULE (device); + FuWacModulePrivate *priv = GET_PRIVATE (self); + g_string_append (str, " FuWacSubModule:\n"); + g_string_append_printf (str, " fw-type:\t\t%s\n", + fu_wac_module_fw_type_to_string (priv->fw_type)); + g_string_append_printf (str, " status:\t\t%s\n", + fu_wac_module_status_to_string (priv->status)); + g_string_append_printf (str, " command:\t\t%s\n", + fu_wac_module_command_to_string (priv->command)); +} + +static gboolean +fu_wac_module_refresh (FuWacModule *self, GError **error) +{ + FuWacDevice *parent_device = FU_WAC_DEVICE (fu_device_get_parent (FU_DEVICE (self))); + FuWacModulePrivate *priv = GET_PRIVATE (self); + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_MODULE, + [1 ... FU_WAC_PACKET_LEN - 1] = 0xff }; + + /* get from hardware */ + if (!fu_wac_device_get_feature_report (parent_device, buf, sizeof(buf), + FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC | + FU_WAC_DEVICE_FEATURE_FLAG_NO_DEBUG, + error)) { + g_prefix_error (error, "failed to refresh status: "); + return FALSE; + } + + /* check fw type */ + if (priv->fw_type != buf[1]) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Submodule GetFeature fw_Type invalid " + "got 0x%02x expected 0x%02x", + (guint) buf[1], (guint) priv->fw_type); + return FALSE; + } + + /* current phase and status */ + if (priv->command != buf[2] || priv->status != buf[3]) { + priv->command = buf[2]; + priv->status = buf[3]; + g_debug ("command: %s, status: %s", + fu_wac_module_command_to_string (priv->command), + fu_wac_module_status_to_string (priv->status)); + } + + /* success */ + return TRUE; +} + +gboolean +fu_wac_module_set_feature (FuWacModule *self, + guint8 command, + GBytes *blob, /* optional */ + GError **error) +{ + FuWacDevice *parent_device = FU_WAC_DEVICE (fu_device_get_parent (FU_DEVICE (self))); + FuWacModulePrivate *priv = GET_PRIVATE (self); + const guint8 *data; + gsize len = 0; + guint busy_poll_loops = 100; /* 1s */ + guint8 buf[] = { [0] = FU_WAC_REPORT_ID_MODULE, + [1] = priv->fw_type, + [2] = command, + [3 ... FU_WAC_PACKET_LEN - 1] = 0xff }; + + /* verify the size of the blob */ + if (blob != NULL) { + data = g_bytes_get_data (blob, &len); + if (len > 509) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Submodule SetFeature blob larger than " + "buffer %" G_GSIZE_FORMAT, len); + return FALSE; + } + } + + /* build packet */ + if (len > 0) + memcpy (&buf[3], data, len); + + /* tell the daemon the current status */ + switch (command) { + case FU_WAC_MODULE_COMMAND_START: + fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_ERASE); + break; + case FU_WAC_MODULE_COMMAND_DATA: + fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE); + break; + case FU_WAC_MODULE_COMMAND_END: + fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); + break; + default: + break; + } + + /* send to hardware */ + if (!fu_wac_device_set_feature_report (parent_device, buf, len + 3, + FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC, + error)) { + g_prefix_error (error, "failed to set module feature: "); + return FALSE; + } + + /* special case StartProgram, as it can take much longer as it is + * erasing the blocks (15s) */ + if (command == FU_WAC_MODULE_COMMAND_START) + busy_poll_loops *= 15; + + /* wait for hardware */ + for (guint i = 0; i < busy_poll_loops; i++) { + if (!fu_wac_module_refresh (self, error)) + return FALSE; + if (priv->status == FU_WAC_MODULE_STATUS_BUSY) { + g_usleep (10000); /* 10ms */ + continue; + } + if (priv->status == FU_WAC_MODULE_STATUS_OK) + break; + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to SetFeature: %s", + fu_wac_module_status_to_string (priv->status)); + return FALSE; + } + + /* too many retries */ + if (priv->status != FU_WAC_MODULE_STATUS_OK) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Timed out after %u loops with status %s", + busy_poll_loops, + fu_wac_module_status_to_string (priv->status)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_wac_module_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FuWacModule *self = FU_WAC_MODULE (object); + FuWacModulePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_FW_TYPE: + g_value_set_uint (value, priv->fw_type); + break; + case PROP_USB_DEVICE: + g_value_set_object (value, priv->usb_device); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_wac_module_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FuWacModule *self = FU_WAC_MODULE (object); + FuWacModulePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_FW_TYPE: + priv->fw_type = g_value_get_uint (value); + break; + case PROP_USB_DEVICE: + g_set_object (&priv->usb_device, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_wac_module_init (FuWacModule *self) +{ +} + +static void +fu_wac_module_constructed (GObject *object) +{ + FuWacModule *self = FU_WAC_MODULE (object); + FuWacModulePrivate *priv = GET_PRIVATE (self); + g_autofree gchar *devid = NULL; + g_autofree gchar *vendor_id = NULL; + + /* set vendor ID */ + vendor_id = g_strdup_printf ("USB:0x%04X", g_usb_device_get_vid (priv->usb_device)); + fu_device_set_vendor_id (FU_DEVICE (self), vendor_id); + + /* set USB physical and logical IDs */ + fu_device_set_physical_id (FU_DEVICE (self), + g_usb_device_get_platform_id (priv->usb_device)); + fu_device_set_logical_id (FU_DEVICE (self), + fu_wac_module_fw_type_to_string (priv->fw_type)); + + /* append the firmware kind to the generated GUID */ + devid = g_strdup_printf ("USB\\VID_%04X&PID_%04X-%s", + g_usb_device_get_vid (priv->usb_device), + g_usb_device_get_pid (priv->usb_device), + fu_wac_module_fw_type_to_string (priv->fw_type)); + fu_device_add_instance_id (FU_DEVICE (self), devid); + + G_OBJECT_CLASS (fu_wac_module_parent_class)->constructed (object); +} + +static void +fu_wac_module_finalize (GObject *object) +{ + FuWacModule *self = FU_WAC_MODULE (object); + FuWacModulePrivate *priv = GET_PRIVATE (self); + if (priv->usb_device != NULL) + g_object_unref (priv->usb_device); + G_OBJECT_CLASS (fu_wac_module_parent_class)->finalize (object); +} + +static void +fu_wac_module_class_init (FuWacModuleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + + /* properties */ + object_class->get_property = fu_wac_module_get_property; + object_class->set_property = fu_wac_module_set_property; + pspec = g_param_spec_object ("usb-device", NULL, NULL, + G_USB_TYPE_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_USB_DEVICE, pspec); + pspec = g_param_spec_uint ("fw-type", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_FW_TYPE, pspec); + + object_class->constructed = fu_wac_module_constructed; + object_class->finalize = fu_wac_module_finalize; + klass_device->to_string = fu_wac_module_to_string; +} diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-module.h fwupd-1.2.10/plugins/wacom-usb/fu-wac-module.h --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-module.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-module.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WAC_MODULE (fu_wac_module_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuWacModule, fu_wac_module, FU, WAC_MODULE, FuDevice) + +struct _FuWacModuleClass +{ + FuDeviceClass parent_class; +}; + +#define FU_WAC_MODULE_FW_TYPE_TOUCH 0x00 +#define FU_WAC_MODULE_FW_TYPE_BLUETOOTH 0x01 +#define FU_WAC_MODULE_FW_TYPE_EMR_CORRECTION 0x02 +#define FU_WAC_MODULE_FW_TYPE_BLUETOOTH_HID 0x03 +#define FU_WAC_MODULE_FW_TYPE_MAIN 0x3f + +#define FU_WAC_MODULE_COMMAND_START 0x01 +#define FU_WAC_MODULE_COMMAND_DATA 0x02 +#define FU_WAC_MODULE_COMMAND_END 0x03 + +gboolean fu_wac_module_set_feature (FuWacModule *self, + guint8 command, + GBytes *blob, + GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-touch.c fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-touch.c --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-touch.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-touch.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wac-device.h" +#include "fu-wac-module-touch.h" + +#include "fu-chunk.h" +#include "dfu-firmware.h" + +struct _FuWacModuleTouch +{ + FuWacModule parent_instance; +}; + +G_DEFINE_TYPE (FuWacModuleTouch, fu_wac_module_touch, FU_TYPE_WAC_MODULE) + +static gboolean +fu_wac_module_touch_write_firmware (FuDevice *device, + GBytes *blob, + FwupdInstallFlags flags, + GError **error) +{ + DfuElement *element; + DfuImage *image; + FuWacModule *self = FU_WAC_MODULE (device); + gsize blocks_total = 0; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + + /* load .hex file */ + if (!dfu_firmware_parse_data (firmware, blob, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) + return FALSE; + + /* check type */ + if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_INTEL_HEX) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "expected firmware format is 'ihex', got '%s'", + dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); + return FALSE; + } + + /* use the correct image from the firmware */ + image = dfu_firmware_get_image (firmware, 0); + if (image == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no firmware image"); + return FALSE; + } + element = dfu_image_get_element_default (image); + if (element == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no firmware element"); + return FALSE; + } + g_debug ("using element at addr 0x%0x", + (guint) dfu_element_get_address (element)); + blob = dfu_element_get_contents (element); + + /* build each data packet */ + chunks = fu_chunk_array_new_from_bytes (blob, + dfu_element_get_address (element), + 0x0, /* page_sz */ + 128); /* packet_sz */ + blocks_total = chunks->len + 2; + + /* start, which will erase the module */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_START, NULL, error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, 1, blocks_total); + + /* data */ + 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); + guint8 buf[128+7] = { 0xff }; + g_autoptr(GBytes) blob_chunk = NULL; + + /* build G11T data packet */ + memset (buf, 0xff, sizeof(buf)); + buf[0] = 0x01; /* writing */ + buf[1] = chk->idx + 1; + fu_common_write_uint32 (&buf[2], chk->address, G_LITTLE_ENDIAN); + buf[6] = 0x10; /* no idea! */ + memcpy (&buf[7], chk->data, chk->data_sz); + blob_chunk = g_bytes_new (buf, sizeof(buf)); + if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_DATA, + blob_chunk, error)) { + g_prefix_error (error, "failed to write block %u: ", chk->idx); + return FALSE; + } + + /* update progress */ + fu_device_set_progress_full (device, i + 1, blocks_total); + } + + /* end */ + if (!fu_wac_module_set_feature (self, FU_WAC_MODULE_COMMAND_END, NULL, error)) + return FALSE; + + /* update progress */ + fu_device_set_progress_full (device, blocks_total, blocks_total); + return TRUE; +} + +static void +fu_wac_module_touch_init (FuWacModuleTouch *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_install_duration (FU_DEVICE (self), 30); +} + +static void +fu_wac_module_touch_class_init (FuWacModuleTouchClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->write_firmware = fu_wac_module_touch_write_firmware; +} + +FuWacModule * +fu_wac_module_touch_new (GUsbDevice *usb_device) +{ + FuWacModule *module = NULL; + module = g_object_new (FU_TYPE_WAC_MODULE_TOUCH, + "usb-device", usb_device, + "fw-type", FU_WAC_MODULE_FW_TYPE_TOUCH, + NULL); + return module; +} diff -Nru fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-touch.h fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-touch.h --- fwupd-1.0.9/plugins/wacom-usb/fu-wac-module-touch.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/fu-wac-module-touch.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-wac-module.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WAC_MODULE_TOUCH (fu_wac_module_touch_get_type ()) +G_DECLARE_FINAL_TYPE (FuWacModuleTouch, fu_wac_module_touch, FU, WAC_MODULE_TOUCH, FuWacModule) + +FuWacModule *fu_wac_module_touch_new (GUsbDevice *usb_device); + +G_END_DECLS diff -Nru fwupd-1.0.9/plugins/wacom-usb/meson.build fwupd-1.2.10/plugins/wacom-usb/meson.build --- fwupd-1.0.9/plugins/wacom-usb/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,68 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginWacomUsb"'] + +install_data(['wacom-usb.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_wacom_usb', + fu_hash, + sources : [ + 'fu-wac-common.c', + 'fu-wac-device.c', + 'fu-wac-firmware.c', + 'fu-wac-module.c', + 'fu-wac-module-bluetooth.c', + 'fu-wac-module-touch.c', + 'fu-plugin-wacom-usb.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 : [ + libfwupdprivate, + dfu, + ], +) + +if get_option('tests') + testdatadir = join_paths(meson.current_source_dir(), 'tests') + cargs += '-DTESTDATADIR="' + testdatadir + '"' + e = executable( + 'wacom-usb-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-wac-common.c', + 'fu-wac-firmware.c', + ], + include_directories : [ + include_directories('..'), + include_directories('../dfu'), + include_directories('../..'), + include_directories('../../libfwupd'), + include_directories('../../src'), + ], + dependencies : [ + libxmlb, + gio, + gusb, + gudev, + libm, + ], + link_with : [ + dfu, + libfwupdprivate, + ], + c_args : cargs + ) + test('wacom-usb-self-test', e) +endif diff -Nru fwupd-1.0.9/plugins/wacom-usb/README.md fwupd-1.2.10/plugins/wacom-usb/README.md --- fwupd-1.0.9/plugins/wacom-usb/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,41 @@ +Wacom USB Support +================= + +Introduction +------------ + +Wacom provides interactive pen displays, pen tablets, and styluses to equip and +inspire everyone make the world a more creative place. + +From 2016 Wacom has been using a HID-based proprietary flashing algorithm which +has been documented by support team at Wacom and provided under NDA under the +understanding it would be used to build a plugin under a LGPLv2+ licence. + +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 their 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 -Nru fwupd-1.0.9/plugins/wacom-usb/wacom-usb.quirk fwupd-1.2.10/plugins/wacom-usb/wacom-usb.quirk --- fwupd-1.0.9/plugins/wacom-usb/wacom-usb.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/plugins/wacom-usb/wacom-usb.quirk 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,34 @@ + +# 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 diff -Nru fwupd-1.0.9/po/af.po fwupd-1.2.10/po/af.po --- fwupd-1.0.9/po/af.po 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/po/af.po 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,418 @@ +# 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:" + +#. success +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 -Nru fwupd-1.0.9/po/ast.po fwupd-1.2.10/po/ast.po --- fwupd-1.0.9/po/ast.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/ast.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: Asturian (http://www.transifex.com/freedesktop/fwupd/language/ast/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -78,15 +75,6 @@ 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 -Nru fwupd-1.0.9/po/ca.po fwupd-1.2.10/po/ca.po --- fwupd-1.0.9/po/ca.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/ca.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,15 +3,12 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Antoni Bella Pérez , 2017-2018 +# Antoni Bella Pérez , 2017-2019 # Robert Antoni Buj Gelonch , 2017 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: Catalan (http://www.transifex.com/freedesktop/fwupd/language/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,11 +16,116 @@ "Language: ca\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] "Manca %.0f minut" +msgstr[1] "Manquen %.0f minuts" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Actualització ME del consumidor %s" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Actualització del controlador %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "Actualització ME corporativa de %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Actualizació del dispositiu %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Actualització del controlador incorporat %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Actualització ME de %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Actualizació del sistema %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Actualització de %s" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s té actualitzacions de microprogramari:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +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: command description +msgid "Activate devices" +msgstr "Activa els dispositius" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Activa els dispositius pendents" + +msgid "Activate the new firmware on the device" +msgstr "Activa el microprogramari nou al dispositiu" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Activació de l'actualització del microprogramari" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Activa l’actualització del microprogramari per a" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "S'ha afegit" @@ -49,10 +151,14 @@ 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." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Una actualització requereix que s'aturi el sistema per a finalitzar." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Respon sí a totes les preguntes" @@ -61,10 +167,25 @@ msgid "Apply a binary patch" msgstr "Aplica un pedaç binari" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplica les actualizacions de microprogramari" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Microprogramari aprovat:" +msgstr[1] "Microprogramari aprovat:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Connecta un dispositiu amb capacitat DFU en temps real" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Ajunta al mode microprogramari" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -87,6 +208,22 @@ msgstr "Es necessita l'autenticació per a modificar un remot configurat emprat per a les actualitzacions del microprogramari" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Es requereix autenticació per a modificar la configuració del dimoni" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Es requereix autenticació per a establir la llista de microprogramari aprovat" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Es requereix autenticació per a signar les dades emprant el certificat del client" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Es requereix autenticació per a canviar a la nova versió del micropogramari" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Es requereix autenticació per a desbloquejar un dispositiu" @@ -187,6 +324,14 @@ msgid "Detach currently attached DFU capable device" msgstr "Desconecta el dispositiu amb capacitat DFU actualment connectat" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Separa del mode carregador d'arrencada" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "ID del dispositiu" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "S'ha afegit el dispositiu:" @@ -207,11 +352,18 @@ msgid "Devices that were not updated correctly:" msgstr "Els dispositius que no s'han actualitzat correctament:" +msgid "Disabled fwupdate debugging" +msgstr "La depuració del «fwupdate» està inhabilitada" + #. TRANSLATORS: command description msgid "Disables a given remote" msgstr "Inhabilita un remot indicat" #. TRANSLATORS: command line option +msgid "Display version" +msgstr "Mostra la versió" + +#. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "No comprovar si hi ha metadades antigues" @@ -223,6 +375,11 @@ msgid "Do not check for unreported history" msgstr "No comprovar si hi ha un historial sense informar" +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "No escriure a la base de dades de l'historial" + +#. success msgid "Done!" msgstr "Fet!" @@ -237,6 +394,11 @@ 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..." @@ -253,6 +415,14 @@ msgid "Dump information about a binary patch to the screen" msgstr "Bolca la informació sobre un pedaç binari a la pantalla" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "L'ESP especificat no era vàlid" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Habilita el suport per a l'actualització del microprogramari sobre sistemes compatibles" + #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" msgstr "Habilito aquest remot?" @@ -261,10 +431,16 @@ msgid "Enabled" msgstr "Habilitat" +msgid "Enabled fwupdate debugging" +msgstr "La depuració del «fwupdate» està habilitada" + #. TRANSLATORS: command description msgid "Enables a given remote" msgstr "Habilita un remot indicat" +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 "Si habiliteu aquesta funcionalitat, ho fareu sota el vostre propi risc, el qual significa que haureu de posar-vos en contacte amb el fabricant original de l'equip quant a qualsevol problema causat per aquestes actualitzacions. A $OS_RELEASE:BUG_REPORT_URL$, només s'han de presentar els problemes amb el procés d'actualització." + #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." msgstr "Si habiliteu aquest remot, ho fareu sota el vostre propi risc." @@ -289,6 +465,22 @@ msgid "Exit after the engine has loaded" msgstr "Sur una vegada s'hagi carregat el motor" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Ha fallat en connectar amb el dimoni" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "La baixada ha fallat a causa del límit del servidor" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Ha fallat en obtenir els dispositius pendents" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Ha fallat en instal·lar l’actualització del microprogramari" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "No s'han pogut carregar les peculiaritats" @@ -297,6 +489,14 @@ msgid "Failed to parse arguments" msgstr "Ha fallat en analitzar els arguments" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Ha fallat en tornar a arrencar" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Ha fallat en establir el mode de presentació" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "S'està obtenint el fitxer" @@ -321,6 +521,10 @@ msgid "Filename Signature" msgstr "Signatura del nom del fitxer" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Agent de microprogramari" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "URI base del microprogramari" @@ -344,6 +548,19 @@ msgstr[0] "Les metadades del microprogramari no s'han actualitzat durant %u dia i podria ser que no estiguin actualitzades." msgstr[1] "Les metadades del microprogramari no s'han actualitzat durant %u dies i podria ser que no estiguin actualitzades." +msgid "Firmware updates are not supported on this machine." +msgstr "Les actualitzacions de microprogramari no estan admeses en aquesta màquina." + +msgid "Firmware updates are supported on this machine." +msgstr "Les actualitzacions de microprogramari estan admeses en aquesta màquina." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Etiquetes" + +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" @@ -352,10 +569,22 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Obtén tots els dispositius segons la topologia del sistema" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Obtén tots els dispositius i possibles llançaments" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Obté tots els dispositius que admeten actualitzacions de microprogramari" #. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Obtén tots els connectors habilitats registrats amb el sistema" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "Obté la informació sobre un fitxer de microprogramari" @@ -367,6 +596,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Obté el resultat d'aplicar la funció criptogràfica de resum sobre el microprogramari bolcat" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Obtén la llista del microprogramari aprovat." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Obté la llista d'actualitzacions per al maquinari connectat" @@ -388,16 +621,16 @@ msgstr "Està ociós..." #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instal·la un blob de microprogramari en un dispositiu" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Instal·la un fitxer de microprogramari en aquest maquinari" msgid "Install old version of system firmware" msgstr "Instal·la la versió antiga del microprogramari per al sistema" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Instal·la ara les actualitzacions preparades" - msgid "Install signed device firmware" msgstr "Instal·la microprogramari signat per al dispositiu" @@ -410,13 +643,26 @@ msgid "Install unsigned system firmware" msgstr "Instal·la microprogramari sense signar per al sistema" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instal·lació del microprogramari..." + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "S'està instal·lant l'actualització de microprogramari..." +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "S'està intal·lant a %s…" + msgid "Keyring" msgstr " Anell de claus" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Manca menys d'un minut" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Servei de microprogramari del proveïdor Linux (microprogramari estable)" @@ -427,10 +673,18 @@ msgid "List currently attached DFU capable devices" msgstr "Llista els dispositius amb capacitat DFU actualment connectats" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Llista les actualitzacions de microprogramari compatibles" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "S'està carregant..." +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Connectors especíificats manualment a la llista blanca" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "Fusiona múltiples fitxers de microprogramari en un de sol" @@ -447,9 +701,18 @@ msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Les metadades es poden obtenir des del servei de microprogramari del proveïdor Linux." +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "El dimoni i el client no coincideixin, useu %s en el seu lloc" + msgid "Mode" msgstr "Mode" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Modifica un valor de la configuració del dimoni." + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Modifica un remot indicat" @@ -457,6 +720,9 @@ msgid "Modify a configured remote" msgstr "Modifica un remot configurat" +msgid "Modify daemon configuration" +msgstr "Modifica la configuració del dimoni" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Monitora el dimoni pels esdeveniments" @@ -467,15 +733,26 @@ msgid "Name" msgstr "Nom" +msgid "No action specified!" +msgstr "No s'ha especificat cap acció!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "No s'ha detectat cap maquinari amb capacitat per a l'actualització del microprogramari" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "No s'ha trobat cap connector" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Actualment, no hi ha cap remot habilitat, de manera que no hi ha metadades disponibles." +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "No s’han aplicat les actualitzacions" + msgid "OK" msgstr "D'acord" @@ -483,6 +760,14 @@ msgid "Override plugin warning" msgstr "Passa per alt els avisos del connector" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Preferència sobre el camí ESP predeterminat" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Anul·la els avisos i força l'acció" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Contrasenya" @@ -490,6 +775,10 @@ msgid "Payload" msgstr "Carrega útil" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentatge completat" + msgid "Permission denied" msgstr "Permís denegat" @@ -498,6 +787,12 @@ 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" @@ -509,6 +804,10 @@ msgid "Protocol" msgstr "Protocol" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Consulta el suport per a l'actualització del microprogramari" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -526,6 +825,10 @@ msgid "Reading…" msgstr "S'està llegint..." +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "S'està tornant a arencar..." + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Refresca les metadades des del servidor remot" @@ -573,6 +876,10 @@ msgid "Restart now?" msgstr "Reinicio ara?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Reinicio el dimoni per a fer efectiu el canvi?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "S'està reiniciant el dispositiu..." @@ -581,10 +888,22 @@ msgid "Return all the hardware IDs for the machine" msgstr "Retorna tots els ID del maquinari de la màquina" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Executa la rutina de neteja de la composició del connector en usar install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Executa la rutina de preparació de la composició del connector en usar install-blob" + msgid "Runtime" msgstr "Temps d'execució" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Desa l'estat del dispositiu en un fitxer JSON entre les execucions" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Planifica la instal·lació per al següent reinici quan sigui posible" @@ -616,6 +935,10 @@ msgid "Set release version on firmware file" msgstr "Estableix la versió de llançament al fitxer del microprogramari" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Estableix l'indicador de depuració durant l'actualització" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Estableix la mida del microprogramari per a l'objectiu" @@ -628,6 +951,13 @@ msgid "Sets metadata on a firmware file" msgstr "Estableix les metadades en un fitxer de microprogramari" +msgid "Sets the list of approved firmware" +msgstr "Estableix la llista de microprogramari aprovat" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Estableix la llista del microprogramari aprovat." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Comparteix l'historial de microprogramari amb els desenvolupadors" @@ -636,15 +966,23 @@ 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 for a particular domain" +msgstr "Mostra informació detallada del dimoni per a un domini concret" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Mostra la informació de depuració per a tots els fitxers" +msgid "Show debugging information for all domains" +msgstr "Mostra la informació de depuració per a tots els dominis" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Mostra les opcions per a la depuració" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Mostra els dispositius que no són actualitzables" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Mostra la informació de depuració addicional" @@ -656,6 +994,38 @@ msgid "Show plugin verbose information" msgstr "Mostra la informació detallada del connector" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Mostra el registre de depuració del darrer intent d'actualització" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Mostra la informació sobre l'estat de l'actualització del microprogramari" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Aturar-lo ara?" + +msgid "Sign data using the client certificate" +msgstr "Signa les dades emprant el certificat del client" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Signa les dades emprant el certificat del client" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Signa les dades pujades amb el certificat del client" + +msgid "Signature" +msgstr "Signatura" + +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" @@ -673,11 +1043,25 @@ msgid "Target" msgstr "Objectiu" -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 que 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." - -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." +#. 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 "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." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "No hi ha cap microprogramari aprovat." + +#. 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 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." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Aquesta eina només pot ser usada per l'usuari «root»." #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -691,6 +1075,10 @@ msgid "Type" msgstr "Tipus" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitat per al microprogramari UEFI" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -706,6 +1094,15 @@ msgid "Unlocks the device for firmware access" msgstr "Desbloqueja el dispositiu per accedir al microprogramari" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "No estableixis l'indicador de depuració durant l'actualització" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Versió %s no admesa del dimoni, la versió del client és %s" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Suma de verificació de l'actualització" @@ -714,6 +1111,11 @@ 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ó" @@ -734,8 +1136,9 @@ msgid "Update Version" msgstr "Versió de l'actualització" -msgid "Update device firmware on Linux" -msgstr "Actualitza el microprogramari del dispositiu a Linux" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Actualitza tots els dispositius que coincideixin amb les metadades locals" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -753,6 +1156,10 @@ msgstr "Actualitza les metadades emmagatzemades amb el contingut de la ROM actual" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Actualitza les metadades emmagatzemades amb el contingut actual" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Actualitza tot el microprogramari a les versions més recents" @@ -763,6 +1170,11 @@ 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:" @@ -796,6 +1208,10 @@ msgstr "Vigila els dispositius DFU que han estat connectats en calent" #. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Mira per a canvis al maquinari" + +#. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "Escriu el microprogramari des d'un fitxer a dins del dispositiu" @@ -810,6 +1226,3 @@ #. 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 -Nru fwupd-1.0.9/po/cs.po fwupd-1.2.10/po/cs.po --- fwupd-1.0.9/po/cs.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/cs.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,16 +3,13 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Daniel Rusek , 2017 -# Daniel Rusek , 2017 -# Marek Černocký , 2016 +# Ascii Wolf , 2017 +# Ascii Wolf , 2017 +# Marek Černocký , 2016,2018 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: Czech (http://www.transifex.com/freedesktop/fwupd/language/cs/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,6 +17,15 @@ "Language: cs\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Zbývá %.0f minuta" +msgstr[1] "Zbývají %.0f minuty" +msgstr[2] "Zbývá %.0f minut" +msgstr[3] "Zbývá %.0f minuty" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" @@ -31,7 +37,11 @@ #. TRANSLATORS: the age of the metadata msgid "Age" -msgstr "Věk" +msgstr "Stáří" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Odsouhlasit a povolit vzdálený zdroj?" #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format @@ -40,35 +50,56 @@ #. TRANSLATORS: command line option msgid "Allow downgrading firmware versions" -msgstr "Povolit ponížení verze firmwaru" +msgstr "Povolit přechod na nižší verze firmwaru" #. TRANSLATORS: command line option msgid "Allow re-installing existing firmware versions" msgstr "Povolit reinstalaci stávající verze firmwaru" +#. 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." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Na všechny dotazy odpovědět ano" + #. TRANSLATORS: command description msgid "Apply a binary patch" msgstr "Použít binární záplatu" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Použít aktualizace firmwaru" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" -msgstr "Napojit zařízení podporující DFU zpět do běhového režimu" +msgstr "Napojit zařízení podporující DFU zpět do provozního režimu" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Napojit do režimu firmwaru" + +#. TRANSLATORS: device attributes, i.e. things that +#. * the device can do +msgid "Attributes" +msgstr "Vlastnosti" #. TRANSLATORS: waiting for user to authenticate msgid "Authenticating…" -msgstr "Ověřuje se…" +msgstr "Autentizuje se…" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "K ponížení verze firmwaru na výměnném zařízení je vyžadováno ověření" +msgstr "K přechodu na nižší verzi firmwaru na výměnném zařízení je vyžadováno ověření" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" -msgstr "K ponížení verze firmwaru na tomto počítači je vyžadováno ověření" +msgstr "K přechodu na nižší verzi firmwaru na tomto počítači je vyžadováno ověření" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" -msgstr "K úpravě nastaveného vzdáleného zdroje používaného pro aktualizace firmwaru je vyžadováno ověření" +msgstr "Ke změně nastaveného vzdáleného zdroje, který se používá pro aktualizace firmwaru, je vyžadováno ověření" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" @@ -90,6 +121,10 @@ msgid "Build firmware using a sandbox" msgstr "Sestavit firmware za použití izolovaného prostředí" +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Zrušit" + #. TRANSLATORS: this is when a device ctrl+c's a watch msgid "Cancelled" msgstr "Zrušeno" @@ -104,6 +139,10 @@ msgid "Checksum" msgstr "Kontrolní součet" +#. TRANSLATORS: chip ID, e.g. "0x58200204" +msgid "Chip ID" +msgstr "ID čipu" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Vyberte zařízení:" @@ -118,7 +157,7 @@ #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" -msgstr "Smazat všechny aktualizace naplánované pro offline aktualizaci" +msgstr "Smazat vše naplánované pro aktualizaci při odpojení" #. TRANSLATORS: command description msgid "Clears the results from the last update" @@ -136,6 +175,9 @@ msgid "Create a binary patch using two files" msgstr "Vytvořit binární záplatu za použití dvou souborů" +msgid "DFU" +msgstr "DFU" + #. TRANSLATORS: DFU stands for device firmware update msgid "DFU Utility" msgstr "Nástroj pro práci s DFU" @@ -146,7 +188,7 @@ #. TRANSLATORS: decompressing the firmware file msgid "Decompressing…" -msgstr "Rozbaluje se firmware…" +msgstr "Rozbaluje se…" #. TRANSLATORS: command description msgid "Decrypt firmware data" @@ -160,6 +202,10 @@ msgid "Detach currently attached DFU capable device" msgstr "Odpojit aktuálně napojené zařízení podporující DFU" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Odpojit do režimu zavaděče" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Přidáno zařízení:" @@ -172,19 +218,55 @@ msgid "Device removed:" msgstr "Odebráno zařízení:" +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Zařízení, která byla úspěšně aktualizována:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Zařízení, která nebyla úspěšně aktualizována:" + +msgid "Disabled fwupdate debugging" +msgstr "Vypnout ladění fwupdate" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Zakázat zadaný vzdálený zdroj" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Zobrazit verzi" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Nekontrolovat stáří metadat" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Nekontrolovat restart po aktualizaci" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Nekontrolovat nenahlášení historie" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Nezapisovat do databáze historie" + +#. success msgid "Done!" msgstr "Hotovo!" #. TRANSLATORS: command description msgid "Downgrades the firmware on a device" -msgstr "Ponížit verzi firmwaru na zařízení" +msgstr "Přejít na nižší verzi firmwaru na zařízení" #. 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 "Ponižuje se %s z verze %s na %s…" +msgstr "Převádí se %s z verze %s na %s…" #. TRANSLATORS: downloading from a remote server msgid "Downloading…" @@ -192,7 +274,7 @@ #. TRANSLATORS: command description msgid "Dump SMBIOS data from a file" -msgstr "Vypsat SMBIOS data ze souboru" +msgstr "Vypsat data SMBIOS ze souboru" #. TRANSLATORS: command description msgid "Dump details about a firmware file" @@ -202,14 +284,44 @@ msgid "Dump information about a binary patch to the screen" msgstr "Vypsat na obrazovku informace o binární záplatě" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Určený ESP není platný" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Povolit podporu aktualizace firmwaru na podporovaných systémech" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Povolit tento vzdálený zdroj?" + #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Povoleno" +msgid "Enabled fwupdate debugging" +msgstr "Zapnout ladění fwupdate" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Povolit zadaný vzdálený zdroj" + +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 "Zapnutí této funkcionality je na vaše vlastní riziko, což znamená, že v případě problémů způsobených aktualizací musíte kontaktovat přímo výrobce zařízení. Pouze problémy s aktualizačním procesem jako takovým by měly být hlášeny na $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Povolení tohoto zdroje je na vaše vlastní riziko." + #. TRANSLATORS: command description msgid "Encrypt firmware data" msgstr "Zašifrovat data firmwaru" +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Smazat veškerou historii aktualizací" + #. TRANSLATORS: erasing contents of the flash chips msgid "Erasing…" msgstr "Maže se…" @@ -222,6 +334,14 @@ msgid "Exit after the engine has loaded" msgstr "Skončit po načtení výkonné části" +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Nezdařilo se stáhnout kvůli omezením serveru" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Selhalo načtení zvláštních požadavků" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Selhalo zpracování argumentů" @@ -266,6 +386,21 @@ msgid "Firmware Utility" msgstr "Nástroj pro práci s firmwarem" +#. 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] "Metadata firmwaru nebyla akualizována již celý den a nelze je aktualizovat." +msgstr[1] "Metadata firmwaru nebyla akualizována již %u dny a nelze je aktualizovat." +msgstr[2] "Metadata firmwaru nebyla akualizována již %u dní a nelze je aktualizovat." +msgstr[3] "Metadata firmwaru nebyla akualizována již %u dne a nelze je aktualizovat." + +msgid "Firmware updates are not supported on this machine." +msgstr "Aktualizace firmwaru nejsou na tomto počítači podporované." + +msgid "Firmware updates are supported on this machine." +msgstr "Aktualizace firmwaru jsou na tomto počítači podporované." + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Nalezeno" @@ -274,12 +409,20 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Zjistit všechna zařízení podle topologie systému" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Zjistit všechna zařízení podporující aktualizaci firmwaru" #. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Zjistit všechny povolené zásuvné moduly registrované v systému" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" -msgstr "Vypsat podrobnosti o souboru s firmwarem" +msgstr "Zjistit podrobnosti o souboru s firmwarem" #. TRANSLATORS: command description msgid "Gets the configured remotes" @@ -310,16 +453,16 @@ msgstr "Nečinný…" #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Nainstalovat na zařízení binární soubor s firmwarem" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Nainstalovat soubor s firmwarem na tento hardware" msgid "Install old version of system firmware" msgstr "Instalace starší verze systémového firmwaru" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Nainstalovat připravené aktualizace nyní" - msgid "Install signed device firmware" msgstr "Instalace podepsaného firmwaru zařízení" @@ -336,17 +479,40 @@ msgid "Installing firmware update…" msgstr "Instaluje se aktualizace firmwaru…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instaluje se na zařízení %s…" + msgid "Keyring" msgstr "Klíčenka" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Zbývá méně než jedna minuta" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (stabilní firmware)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (testovací firmware)" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "Vypsat právě napojená zařízení podporující DFU" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Vypsat podporované aktualizace firmwarů" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Načítá se…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Ručně povolit konkrétní zásuvné moduly" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "Sloučit více firmwarů do jednoho" @@ -359,19 +525,23 @@ msgid "Metadata URI Signature" msgstr "Podpis URI metadat" +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadata lze získat ze služby Linux Vendor Firmware." + msgid "Mode" msgstr "Režim" #. TRANSLATORS: command description msgid "Modifies a given remote" -msgstr "Upraví zadaný vzdálený zdroj" +msgstr "Změnit zadaný vzdálený zdroj" msgid "Modify a configured remote" msgstr "Upravit nastavený vzdálený zdroj" #. TRANSLATORS: command description msgid "Monitor the daemon for events" -msgstr "Sledovat události démonu" +msgstr "Sledovat události démona" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' @@ -379,11 +549,22 @@ msgid "Name" msgstr "Název" +msgid "No action specified!" +msgstr "Není určena žádné akce!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nebylo nalezeno žádné zařízení schopné aktualizace firmwaru" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nebyl nalezen žádný zásuvný modul" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Zrovna nejsou povolené žádné vzdálené zdroje, takže nejsou k dispozici žádná metadata." + msgid "OK" msgstr "V pořádku" @@ -391,18 +572,40 @@ msgid "Override plugin warning" msgstr "Potlačit varování zásuvného modulu" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Přepsat výchozí cestu ESP" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Heslo" +msgid "Payload" +msgstr "Obsah" + +msgid "Permission denied" +msgstr "Byl odepřen přístup" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Zadejte prosím číslo od 0 do %u:" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Priorita" +msgid "Proceed with upload?" +msgstr "Pokračovat v nahrávání?" + #. TRANSLATORS: DFU protocol version, e.g. 1.1 msgid "Protocol" msgstr "Protokol" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Dotázat se na podporu aktualizace firmwaru" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -441,7 +644,7 @@ #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" -msgstr "Vzdálené ID" +msgstr "ID vzdáleného zdroje" #. TRANSLATORS: this is when a device is hotplugged msgid "Removed" @@ -449,7 +652,23 @@ #. TRANSLATORS: command description msgid "Replace data in an existing firmware file" -msgstr "Nahradit data v existujícím souboru s firmwarem" +msgstr "Nahradit data ve stávajícím souboru s firmwarem" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI hlášení" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Vyžaduje připojení k Internetu" + +#. TRANSLATORS: command description +msgid "Reset a DFU device" +msgstr "Resetovat zařízení podporující DFU" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Restartovat nyní?" #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" @@ -459,6 +678,9 @@ msgid "Return all the hardware IDs for the machine" msgstr "Vrátit všechna ID hardwaru počítače" +msgid "Runtime" +msgstr "provozní" + #. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Pokud je to možné, naplánovat instalaci na příští restart" @@ -491,6 +713,10 @@ msgid "Set release version on firmware file" msgstr "Nastavit verzi vydání v souboru s firmwarem" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Během aktualizace nastavit příznak ladění" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Nastavit velikost firmwaru pro cíl" @@ -503,22 +729,42 @@ msgid "Sets metadata on a firmware file" msgstr "Nastavit metadata v souboru s firmwarem" +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Sdílet historii firmwaru s vývojáři" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" -msgstr "Zobrazit verzi klienta a démonu" - -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Zobrazovat ladicí informace pro všechny soubory" +msgstr "Zobrazit verzi klienta a démona" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Zobrazit volby ladění" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Zobrazit zařízení, která nelze aktualizovat" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Zobrazovat doplňující informace pro ladění" +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Zobrazit historii aktualizací firmwaru" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Zobrazit podrobné informace o zásuvném modulu" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Zobrazit ladicí záznam z posledního pokusu o aktualizaci" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Zobrazit informace o stavu aktualizace firmwaru" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Stav" @@ -529,11 +775,23 @@ msgid "Status" msgstr "Stav" -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žno jej použít také na telefonech, tabletech a serverech bez monitoru." +#. TRANSLATORS: section header for the release one line summary +msgid "Summary" +msgstr "Souhrn" + +msgid "Target" +msgstr "Cíl" + +#. 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 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í." + +#. 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 učinit aktualizaci firmwaru na Linuxu automatickou, bezpečnou a spolehlivou. Pro zobrazení a aplikaci aktualizací můžete použít GUI správce softwaru typu GNOME Software, nástroj pro příkazovou řádku nebo přímo 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í." #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -547,6 +805,10 @@ msgid "Type" msgstr "Typ" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Nástroj pro práci s firmwarem UEFI" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -562,6 +824,10 @@ msgid "Unlocks the device for firmware access" msgstr "Odemknout zařízení pro přístup k firmwaru" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Během aktualizace zrušit příznak ladění" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Kontrolní součet aktualizace" @@ -574,16 +840,29 @@ msgid "Update Location" msgstr "Umístění aktualizace" +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "Název aktualizace" + #. TRANSLATORS: section header for remote ID, e.g. lvfs-testing msgid "Update Remote ID" -msgstr "Vzdálené ID aktualizace" +msgstr "ID vzdáleného zdroje aktualizace" + +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "Souhrn aktualizace" #. TRANSLATORS: section header for firmware version msgid "Update Version" msgstr "Verze aktualizace" -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:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Aktualizovat nyní?" msgid "Update the stored device verification information" msgstr "Aktualizace uložené informace o ověření zařízení" @@ -603,6 +882,18 @@ msgid "Updating %s from %s to %s... " msgstr "Aktualizuje se %s z verze %s na %s…" +#. TRANSLATORS: the server sent the user a small message +msgid "Upload message:" +msgstr "Nahraná zpráva:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Nahrát hlášení nyní?" + +#. 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 "Když nahrajete hlášení o firmwaru, pomůžete tím výrobcům hardwaru rychle rozpoznat nezdařené a úspěšné aktualizace na reálných zařízeních." + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Uživatelské jméno" @@ -615,11 +906,19 @@ msgid "Version" msgstr "Verze" +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Čeká se…" + #. TRANSLATORS: command description msgid "Watch DFU devices being hotplugged" msgstr "Sledovat připojení zařízení podporujících DFU" #. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Sledovat změny hardwaru" + +#. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "Zapsat firmware ze souboru do zařízení" @@ -631,5 +930,6 @@ msgid "Writing…" msgstr "Zapisuje se…" -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 "Váš distributor nemusí schválit některé aktualizace firmwaru kvůli kompatibilitě s vaším systémem nebo připojenými zařízeními." diff -Nru fwupd-1.0.9/po/da.po fwupd-1.2.10/po/da.po --- fwupd-1.0.9/po/da.po 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/po/da.po 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,1228 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# scootergrisen, 2019 +# scootergrisen, 2019 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Danish (http://www.transifex.com/freedesktop/fwupd/language/da/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: da\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minut tilbage" +msgstr[1] "%.0f minutter tilbage" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s ME-opdatering for forbruger" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Kontrolleropdatering for %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s ME-opdatering for virksomhed" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Enhedsopdatering for %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Controlleropdatering for indlejret %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "ME-opdatering for %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Systemopdatering for %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Opdatering for %s" + +#. TRANSLATORS: first replacement is device name +#, c-format +msgid "%s has firmware updates:" +msgstr "%s har firmwareopdateringer:" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dag" +msgstr[1] "%u dage" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u time" +msgstr[1] "%u timer" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minut" +msgstr[1] "%u minutter" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekund" +msgstr[1] "%u sekunder" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktivér enheder" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktiverer afventende enheder" + +msgid "Activate the new firmware on the device" +msgstr "Aktivér den nye firmware på enheden" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktiverer firmwareopdatering" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktiverer firmwareopdatering for" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Tilføjet" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Alder" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Accepter og aktivér fjernen?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias til %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Tillad nedgradering af firmwareversioner" + +#. TRANSLATORS: command line option +msgid "Allow re-installing existing firmware versions" +msgstr "Tillad geninstallering af eksisterende firmwareversioner" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "For at fuldføre en opdatering skal systemet genstartes." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "For at fuldføre en opdatering skal systemet lukkes ned." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Svar ja til alle spørgsmål" + +#. TRANSLATORS: command description +msgid "Apply a binary patch" +msgstr "Anvend en binær patch" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Anvend firmwareopdateringer" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Godkendt firmware:" +msgstr[1] "Godkendt firmware:" + +#. TRANSLATORS: command description +msgid "Attach DFU capable device back to runtime" +msgstr "Tilkobl enheder som formår DFU tilbage til runtime" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Tilkobl til firmwaretilstand" + +#. TRANSLATORS: device attributes, i.e. things that +#. * the device can do +msgid "Attributes" +msgstr "Attributter" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autentificerer …" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Der kræves autentifikation for at nedgradere firmwaren på en flytbar enhed" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Der kræves autentifikation for at nedgradere firmwaren på maskinen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Der kræves autentifikation for at redigere en konfigureret fjern som bruges til firmwareopdateringer" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Der kræves autentifikation for at redigere dæmonkonfiguration" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Der kræves autentifikation for at indstille listen over godkendt firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Der kræves autentifikation for at underskrive data med klientcertifikatet" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Der kræves autentifikation for at skifte til den nye firmwareversion" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Der kræves autentifikation for at låse enhed op" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Der kræves autentifikation for at opdatere firmwaren på en flytbar enhed" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Der kræves autentifikation for at opdatere firmwaren på maskinen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Der kræves autentifikation for at opdatere de gemte checksumme for enheden" + +#. TRANSLATORS: command description +msgid "Build firmware using a sandbox" +msgstr "Byg firmware med en sandkasse" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Annuller" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Annulleret" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Ændret" + +#. TRANSLATORS: section header for firmware checksum +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Checksum" + +#. TRANSLATORS: chip ID, e.g. "0x58200204" +msgid "Chip ID" +msgstr "Chip-id" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Vælg en enhed:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Vælg en udgivelse:" + +#. TRANSLATORS: this is the encryption method used when writing +msgid "Cipher" +msgstr "Krypteringsalgoritme" + +#. TRANSLATORS: command description +msgid "Clears any updates scheduled to be updated offline" +msgstr "Rydder opdateringer som er planlagt til at blive opdateret offline" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Rydder resultaterne fra den sidste opdatering" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Kommandoen blev ikke fundet" + +#. TRANSLATORS: command description +msgid "Convert firmware to DFU format" +msgstr "Konverter firmware til DFU-format" + +#. TRANSLATORS: command description +msgid "Create a binary patch using two files" +msgstr "Opret en binær patch ved brug af to filer" + +msgid "DFU" +msgstr "DFU" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU-redskab" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Fejlsøgningsindstillinger" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Udpakker …" + +#. TRANSLATORS: command description +msgid "Decrypt firmware data" +msgstr "Afkryptér firmwaredata" + +#. TRANSLATORS: section header for firmware description +msgid "Description" +msgstr "Beskrivelse" + +#. TRANSLATORS: command description +msgid "Detach currently attached DFU capable device" +msgstr "Frakobl tilkoblede enheder som formår DFU" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Frakobl til opstartsindlæsertilstand" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Enheds-id" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Enhed tilføjet:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Enhed ændret:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Enhed fjernet:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Enheder som det lykkedes at opdatere:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Enheder som ikke blev opdateret korrekt:" + +msgid "Disabled fwupdate debugging" +msgstr "Deaktivér fejlsøgning af fwupdate" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Deaktiverer en angivet fjern" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Vis version" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Tjek ikke efter gammel metadata" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Tjek ikke om der er genstartet, efter opdatering" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Tjek ikke efter historik som ikke er blevet rapporteret" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Skriv ikke til historikdatabasen" + +#. success +msgid "Done!" +msgstr "Færdig!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Nedgraderer firmwaren på en enhed" + +#. 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 "Nedgraderer %s fra %s til %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Nedgraderer %s …" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Downloader …" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Dump SMBIOS-data fra en fil" + +#. TRANSLATORS: command description +msgid "Dump details about a firmware file" +msgstr "Dump detaljer om en firmwarefil" + +#. TRANSLATORS: command description +msgid "Dump information about a binary patch to the screen" +msgstr "Dump information om en binær patch på skærmen" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Angivet ESP var ikke gyldig" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Aktivér understøttelse af firmwareopdateringer på systemer som understøtter det" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Aktivér fjernen?" + +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Aktiveret" + +msgid "Enabled fwupdate debugging" +msgstr "Aktivér fejlsøgning af fwupdate" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Aktiverer en angivet fjern" + +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 "Aktivering af funktionen sker på egen risiko. Det betydet at du skal kontakte din oprindelige udstyrsproducent vedrørende eventuelle problemer forårsaget af opdateringerne. Det er kun problemer med selv opdateringsprocessen som skal indsende på $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Aktivering af fjernen sker på egen risiko." + +#. TRANSLATORS: command description +msgid "Encrypt firmware data" +msgstr "Kryptér firmwaredata" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Slet al historik over firmwareopdateringer" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Sletter …" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Afslut efter en lille forsinkelse" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Afslut efter motoren er indlæst" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Kunne ikke oprette forbindelse til dæmonen" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Kunne ikke downloade pga. begrænsning på server" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Kunne ikke hente afventende enheder" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Kunne ikke installere firmwareopdateringen" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Kunne ikke indlæse quirks" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Kunne ikke fortolke argumenter" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Kunne ikke genstarte" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Kunne ikke indstille splash-tilstand" + +#. TRANSLATORS: downloading unknown file +msgid "Fetching file" +msgstr "Henter fil" + +#. TRANSLATORS: downloading new firmware file +msgid "Fetching firmware" +msgstr "Henter firmware" + +#. TRANSLATORS: downloading new metadata file +msgid "Fetching metadata" +msgstr "Henter metadata" + +#. TRANSLATORS: downloading new signing file +msgid "Fetching signature" +msgstr "Henter underskrift" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Filnavn" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Filnavnets underskrift" + +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Firmwareagent" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Grund-URI for firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "D-Bus-tjeneste for firmwareopdatering" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Firmwareopdateringsdæmon" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Firmwareredskab" + +#. 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] "Firmwaremetadata er ikke blevet opdateret i %u dag og kan være forældet." +msgstr[1] "Firmwaremetadata er ikke blevet opdateret i %u dage og kan være forældet." + +msgid "Firmware updates are not supported on this machine." +msgstr "Maskinen understøtter ikke firmwareopdateringer." + +msgid "Firmware updates are supported on this machine." +msgstr "Maskinen understøtter firmwareopdateringer." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Flag" + +msgid "Force the action ignoring all warnings" +msgstr "Tving handlingen og ignorer alle advarsler" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Fundet" + +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Hent alle enheder i henhold til systemets topologi" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Hent alle enheder og mulige udgivelser" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Hent alle enheder som understøtter firmwareopdateringer" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Hent alle aktiverede plugins som er registreret med systemet" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Hent detaljer om en firmwarefil" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Henter de konfigurerede fjerne" + +#. TRANSLATORS: command description +msgid "Gets the cryptographic hash of the dumped firmware" +msgstr "Henter den kryptografiske hash fra den dumpede firmware" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Henter listen over godkendt firmware." + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Henter listen over opdateringer for tilsluttet hardware" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Henter resultaterne fra en enhed" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Henter resultaterne fra den sidste opdatering" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "Id" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Inaktiv …" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Installer en firmwareblob på en enhed" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Installer en firmwarefil på hardwaren" + +msgid "Install old version of system firmware" +msgstr "Installer gammel version af systemfirmware" + +msgid "Install signed device firmware" +msgstr "Installer enhedsfirmware der er underskrevet" + +msgid "Install signed system firmware" +msgstr "Installer systemfirmware der er underskrevet" + +msgid "Install unsigned device firmware" +msgstr "Installer enhedsfirmware der ikke er underskrevet" + +msgid "Install unsigned system firmware" +msgstr "Installer systemfirmware der ikke er underskrevet" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Installerer firmware …" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Installerer firmwareopdateringer …" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installerer på %s …" + +msgid "Keyring" +msgstr "Nøglering" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Mindre end et minut tilbage" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (stabilt firmware)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (testning firmware)" + +#. TRANSLATORS: command description +msgid "List currently attached DFU capable devices" +msgstr "Vis tilkoblede enheder som formår DFU" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Vis understøttede firmwareopdateringer" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Indlæser …" + +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Manuel hvidlistning af bestemte plugins" + +#. TRANSLATORS: command description +msgid "Merge multiple firmware files into one" +msgstr "Sammenlæg flere firmwarefiler i én" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metadata-URI" + +#. TRANSLATORS: remote URI +msgid "Metadata URI Signature" +msgstr "Underskrift for metadata-URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadata kan hentes fra Linux Vendor Firmware Service." + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Dæmon og klient passer ikke sammen, brug %s i stedet" + +msgid "Mode" +msgstr "Tilstand" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Rediger en værdi i dæmonkonfiguration." + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Redigerer en angivet fjern" + +msgid "Modify a configured remote" +msgstr "Rediger en konfigureret fjern" + +msgid "Modify daemon configuration" +msgstr "Rediger dæmonkonfiguration" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Overvåg dæmonen for hændelser" + +#. TRANSLATORS: interface name, e.g. "Flash" +#. TRANSLATORS: device name, e.g. 'ColorHug2' +#. TRANSLATORS: section header for the release name +msgid "Name" +msgstr "Navn" + +msgid "No action specified!" +msgstr "Der er ikke angivet nogen handling!" + +#. TRANSLATORS: nothing attached +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Der blev ikke fundet nogen hardware med firmware som kan opdateres" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Der blev ikke fundet nogen plugins" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Der er ikke nogen metadata da der ikke er aktiveret nogen fjerne." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Der blev ikke anvendt nogen opdateringer" + +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: command line option +msgid "Override plugin warning" +msgstr "Tilsidesæt advarsel for plugin" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Tilsidesæt standard-ESP-stien" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Tilsidesæt advarsler og gennemtving handlingen" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Adgangskode" + +msgid "Payload" +msgstr "Nyttelast" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Procent fuldført" + +msgid "Permission denied" +msgstr "Tilladelse nægtet" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Indtast venligst et tal nummer 0 og %u: " + +msgid "Print the version number" +msgstr "Udskriv versionsnummer" + +msgid "Print verbose debug statements" +msgstr "Udskriv uddybende fejlsøgningsudsagn" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Prioritet" + +msgid "Proceed with upload?" +msgstr "Fortsæt med upload?" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Protokol" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Forespørg om understøttelse af firmwareopdatering" + +#. TRANSLATORS: device quirks, i.e. things that +#. * it does that we have to work around +msgid "Quirks" +msgstr "Quirks" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Læs firmware fra enhed ind i en fil" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Læs firmware fra en partition ind i en fil" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Læser …" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Genstarter …" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Genopfrisk metadata fra fjernserver" + +#. TRANSLATORS: these are areas of memory on the chip +msgid "Region" +msgstr "Område" + +#. 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 "Geninstallerer %s med %s... " + +#. TRANSLATORS: section header for the remote the file is coming from +msgid "Remote" +msgstr "Fjern" + +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Fjern-id" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Fjernet" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Erstat data i en eksisterende firmwarefil" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Rapport-URI" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Kræver internetforbindelse" + +#. TRANSLATORS: command description +msgid "Reset a DFU device" +msgstr "Nulstil en DFU-enhed" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Genstart nu?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Genstart dæmonen så ændringerne kan træde i kraft?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Genstarter enhed …" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Returner alle maskinens hardware-id'er" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Kør oprydningsrutinen for pluginkomposition når install-blob bruges" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Kør forberedelsesrutinen for pluginkomposition når install-blob bruges" + +msgid "Runtime" +msgstr "Runtime" + +#. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Gem enhedens tilstand i en JSON-fil mellem udførsler" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Planlægning af installation ved næste genstart, når det er muligt" + +#. TRANSLATORS: scheduing an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planlægger …" + +#. TRANSLATORS: serial number, e.g. '00012345' +msgid "Serial" +msgstr "Serienummer" + +#. TRANSLATORS: command description +msgid "Set alternative name on firmware file" +msgstr "Indstil alternativt navn på firmwarefil" + +#. TRANSLATORS: command description +msgid "Set alternative number on firmware file" +msgstr "Indstil alternativt tal på firmwarefil" + +#. TRANSLATORS: command description +msgid "Set element address on firmware file" +msgstr "Indstil elementadresse på firmwarefil" + +#. TRANSLATORS: command description +msgid "Set product ID on firmware file" +msgstr "Indstil producent-id på firmwarefil" + +#. TRANSLATORS: command description +msgid "Set release version on firmware file" +msgstr "Indstil udgivelsesversion på firmwarefil" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Indstil fejlsøgningsflaget under opdatering" + +#. TRANSLATORS: command description +msgid "Set the firmware size for the target" +msgstr "Indstil firmwarestørrelse på målet" + +#. TRANSLATORS: command description +msgid "Set vendor ID on firmware file" +msgstr "Indstil producent-id på firmwarefil" + +#. TRANSLATORS: command description +msgid "Sets metadata on a firmware file" +msgstr "Indstiller metadata på en firmwarefil" + +msgid "Sets the list of approved firmware" +msgstr "Indstiller listen over godkendt firmware" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Indstiller listen over godkendt firmware." + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Del historik over firmware med udviklerne" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Vis klient- og dæmonversioner" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Vis uddybende information for dæmon for et bestemt domæne" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Vis fejlsøgningsinformation for alle domæner" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Vis fejlsøgningsindstillinger" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Vis enheder som ikke kan opdateres" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Vis ekstra fejlsøgningsinformation" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Vis historik over firmwareopdateringer" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Vis uddybende information om plugin" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Vis fejlsøgningsloggen fra den opdatering der blev forsøgt sidst" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Vis informationen om firmwareopdateringsstatus" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Luk ned nu?" + +msgid "Sign data using the client certificate" +msgstr "Underskriv data med klientcertifikat" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Underskriv data med klientcertifikat" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Underskriv den uploadede data med klientcertifikatet" + +msgid "Signature" +msgstr "Underskrift" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Angiv producent-/produkt-id('er) for DFU-enhed" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Angiv antal bytes pr. USB-overførsel" + +#. TRANSLATORS: device state, i.e. appIDLE +msgid "State" +msgstr "Tilstand" + +#. 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 "Opsummering" + +msgid "Target" +msgstr "Mål" + +#. 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'en er en gratis tjeneste der opererer som en selvstændig juridisk enhed og har ingen forbindelse med $OS_RELEASE:NAME$. Din distributør har måske ikke bekræftet nogen af firmwareopdateringerne for kompatibilitet med dit system eller tilsluttede enheder. Al firmware leveres kun af den oprindelige udstyrsproducent." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Der er ikke nogen godkendt firmware." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Programmet virker måske kun 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 "Fjernen indeholder firmware som der ikke er embargo på, men som stadigvæk testes af hardwareproducenten. Du skal sikre dig at du har en manuel måde til at nedgradere firmwaren på hvis firmwareopdateringen mislykkedes." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Værktøjet kan kun bruges af root-brugeren" + +#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" +msgid "Title" +msgstr "Titel" + +#. TRANSLATORS: transfer size in bytes +msgid "Transfer Size" +msgstr "Overførselsstørrelse" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Type" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI-firmwareredskab" + +#. TRANSLATORS: section header for firmware URI +msgid "URI" +msgstr "URI" + +#. TRANSLATORS: currect daemon status is unknown +msgid "Unknown" +msgstr "Ukendt" + +msgid "Unlock the device to allow access" +msgstr "Lås enheden op for at tillade adgang" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Låser op for enheden for at få adgang til firmwaren" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Fjern fejlsøgningsflaget under opdatering" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Uunderstøttet dæmonversion %s, klientversionen er %s" + +#. TRANSLATORS: section header for firmware checksum +msgid "Update Checksum" +msgstr "Opdateringens checksum" + +#. TRANSLATORS: section header for long firmware desc +msgid "Update Description" +msgstr "Opdateringens beskrivelse" + +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Opdateringens varighed" + +#. TRANSLATORS: section header for firmware remote http:// +msgid "Update Location" +msgstr "Opdateringens placering" + +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "Opdateringens navn" + +#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing +msgid "Update Remote ID" +msgstr "Opdateringens fjern-id" + +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "Opdateringens opsummering" + +#. TRANSLATORS: section header for firmware version +msgid "Update Version" +msgstr "Opdateringens version" + +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Opdater alle enheder som matcher lokale metadata" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Opdateringer som mislykkes er et velkendt problem. Besøg URL'en for mere information:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Opdater nu?" + +msgid "Update the stored device verification information" +msgstr "Opdaterer de gemte informationer om enhedsverifikation" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current ROM contents" +msgstr "Opdater de gemte metadata med indholdet fra den nuværende ROM" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Opdater det gemte metadata med det nuværende indhold" + +#. TRANSLATORS: command description +msgid "Updates all firmware to latest versions available" +msgstr "Opdaterer alle firmware til de seneste versioner" + +#. 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 "Opdaterer %s fra %s til %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Opdaterer %s …" + +#. TRANSLATORS: the server sent the user a small message +msgid "Upload message:" +msgstr "Uploadmeddelelse:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Upload rapport nu?" + +#. 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 "Upload af firmwarerapporter hjælper hardwareproducenter til hurtigt at identificere opdateringer som fejlede og lykkedes på rigtigt hardware." + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Brugernavn" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verificerer …" + +#. TRANSLATORS: section header for release version number +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Venter …" + +#. TRANSLATORS: command description +msgid "Watch DFU devices being hotplugged" +msgstr "Hold øje med DFU-enheder som hotplugges" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Hold øje med hardwareændringer" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Skriv firmware fra fil ind i enhed" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Skriv firmware fra fil ind i en partition" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +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 har måske ikke verificeret kompatibiliteten af firmwareopdateringerne med dit system eller tilsluttede enheder." diff -Nru fwupd-1.0.9/po/de.po fwupd-1.2.10/po/de.po --- fwupd-1.0.9/po/de.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/de.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,15 +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" -"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: German (http://www.transifex.com/freedesktop/fwupd/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,15 +17,26 @@ "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" @@ -35,23 +44,48 @@ #. 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" @@ -59,11 +93,23 @@ #. 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" @@ -79,6 +125,18 @@ 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" @@ -95,9 +153,16 @@ 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" @@ -109,7 +174,7 @@ #. TRANSLATORS: command description msgid "Decrypt firmware data" -msgstr "Firmwaredaten entschlüsseln" +msgstr "Firmware-Daten entschlüsseln" #. TRANSLATORS: section header for firmware description msgid "Description" @@ -131,23 +196,102 @@ 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" + +#. success 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" @@ -161,17 +305,61 @@ 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" @@ -181,10 +369,18 @@ 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" @@ -197,6 +393,10 @@ 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" @@ -209,16 +409,16 @@ 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" msgid "Install old version of system firmware" msgstr "Alte Version der System-Firmware installieren" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Vorbereitete Aktualisierungen jetzt installieren" - msgid "Install signed device firmware" msgstr "Signierte Geräte-Firmware installieren" @@ -231,10 +431,36 @@ 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 …" @@ -243,12 +469,24 @@ 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' @@ -256,18 +494,65 @@ 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" @@ -281,6 +566,10 @@ 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" @@ -300,10 +589,41 @@ 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 …" @@ -334,7 +654,7 @@ #. 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" @@ -344,18 +664,45 @@ msgid "Sets metadata on a firmware file" msgstr "Metadaten einer Firmware-Datei festlegen" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Debuginformationen für alle Dateien anzeigen" +#. 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: for the --verbose arg 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" @@ -366,6 +713,41 @@ 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" @@ -389,10 +771,29 @@ 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" @@ -408,19 +809,48 @@ 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" @@ -431,3 +861,7 @@ #. 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 -Nru fwupd-1.0.9/po/en_GB.po fwupd-1.2.10/po/en_GB.po --- fwupd-1.0.9/po/en_GB.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/en_GB.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,14 +3,11 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Richard Hughes , 2015,2017-2018 +# Richard Hughes , 2015,2017-2019 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: 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" @@ -18,11 +15,46 @@ "Language: en_GB\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 remaining" +msgstr[1] "%.0f minutes remaining" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s has firmware updates:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u day" +msgstr[1] "%u days" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hour" +msgstr[1] "%u hours" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minute" +msgstr[1] "%u minutes" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u second" +msgstr[1] "%u seconds" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Added" @@ -31,6 +63,10 @@ msgid "Age" msgstr "Age" +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Agree and enable the remote?" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" @@ -44,6 +80,14 @@ msgid "Allow re-installing existing firmware versions" msgstr "Allow re-installing existing firmware versions" +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "An update requires a reboot to complete." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "An update requires the system to shutdown to complete." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Answer yes to all questions" @@ -52,10 +96,18 @@ msgid "Apply a binary patch" msgstr "Apply a binary patch" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Apply firmware updates" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Attach DFU capable device back to runtime" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Attach to firmware mode" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -97,6 +149,10 @@ msgid "Build firmware using a sandbox" msgstr "Build firmware using a sandbox" +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Cancel" + #. TRANSLATORS: this is when a device ctrl+c's a watch msgid "Cancelled" msgstr "Cancelled" @@ -174,6 +230,10 @@ msgid "Detach currently attached DFU capable device" msgstr "Detach currently attached DFU capable device" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Detach to bootloader mode" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Device added:" @@ -194,14 +254,34 @@ msgid "Devices that were not updated correctly:" msgstr "Devices that were not updated correctly:" +msgid "Disabled fwupdate debugging" +msgstr "Disabled fwupdate debugging" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Disables a given remote" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Display version" + #. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Do not check for old metadata" #. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Do not check for reboot after update" + +#. TRANSLATORS: command line option msgid "Do not check for unreported history" msgstr "Do not check for unreported history" +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Do not write to the history database" + +#. success msgid "Done!" msgstr "Done!" @@ -216,6 +296,11 @@ msgid "Downgrading %s from %s to %s... " msgstr "Downgrading %s from %s to %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Downgrading %s…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Downloading…" @@ -232,10 +317,36 @@ msgid "Dump information about a binary patch to the screen" msgstr "Dump information about a binary patch to the screen" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "ESP specified was not valid" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Enable firmware update support on supported systems" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Enable this remote?" + #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Enabled" +msgid "Enabled fwupdate debugging" +msgstr "Enabled fwupdate debugging" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Enables a given remote" + +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 "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$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Enabling this remote is done at your own risk." + #. TRANSLATORS: command description msgid "Encrypt firmware data" msgstr "Encrypt firmware data" @@ -256,6 +367,10 @@ msgid "Exit after the engine has loaded" msgstr "Exit after the engine has loaded" +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Failed to download due to server limit" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Failed to load quirks" @@ -304,6 +419,22 @@ msgid "Firmware Utility" msgstr "Firmware Utility" +#. 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 metadata has not been updated for %u day and may not be up to date." +msgstr[1] "Firmware metadata has not been updated for %u days and may not be up to date." + +msgid "Firmware updates are not supported on this machine." +msgstr "Firmware updates are not supported on this machine." + +msgid "Firmware updates are supported on this machine." +msgstr "Firmware updates are supported on this machine." + +msgid "Force the action ignoring all warnings" +msgstr "Force the action ignoring all warnings" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Found" @@ -312,10 +443,18 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Get all devices according to the system topology" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Get all devices that support firmware updates" #. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Get all enabled plugins registered with the system" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "Gets details about a firmware file" @@ -348,16 +487,16 @@ msgstr "Idle…" #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Install a firmware blob on a device" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Install a firmware file on this hardware" msgid "Install old version of system firmware" msgstr "Install old version of system firmware" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Install prepared updates now" - msgid "Install signed device firmware" msgstr "Install signed device firmware" @@ -374,17 +513,40 @@ msgid "Installing firmware update…" msgstr "Installing firmware update…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installing on %s…" + msgid "Keyring" msgstr "Keyring" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Less than one minute remaining" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (stable firmware)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (testing firmware)" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "List currently attached DFU capable devices" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "List supported firmware updates" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Loading…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Manually whitelist specific plugins" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "Merge multiple firmware files into one" @@ -397,6 +559,10 @@ msgid "Metadata URI Signature" msgstr "Metadata URI Signature" +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadata can be obtained from the Linux Vendor Firmware Service." + msgid "Mode" msgstr "Mode" @@ -417,11 +583,22 @@ msgid "Name" msgstr "Name" +msgid "No action specified!" +msgstr "No action specified!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "No hardware detected with firmware update capability" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "No plugins found" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "No remotes are currently enabled so no metadata is available." + msgid "OK" msgstr "OK" @@ -429,6 +606,10 @@ msgid "Override plugin warning" msgstr "Override plugin warning" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Override the default ESP path" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Password" @@ -439,6 +620,17 @@ msgid "Permission denied" msgstr "Permission denied" +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Please enter a number from 0 to %u: " + +msgid "Print the version number" +msgstr "Print the version number" + +msgid "Print verbose debug statements" +msgstr "Print verbose debug statements" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Priority" @@ -450,6 +642,10 @@ msgid "Protocol" msgstr "Protocol" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Query for firmware update support" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -510,6 +706,10 @@ msgid "Reset a DFU device" msgstr "Reset a DFU device" +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Restart now?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Restarting device…" @@ -518,6 +718,14 @@ msgid "Return all the hardware IDs for the machine" msgstr "Return all the hardware IDs for the machine" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Run the plugin composite cleanup routine when using install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Run the plugin composite prepare routine when using install-blob" + msgid "Runtime" msgstr "Runtime" @@ -553,6 +761,10 @@ msgid "Set release version on firmware file" msgstr "Set release version on firmware file" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Set the debugging flag during update" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Set the firmware size for the target" @@ -573,15 +785,15 @@ msgid "Show client and daemon versions" msgstr "Show client and daemon versions" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Show debugging information for all files" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Show debugging options" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Show devices that are not updatable" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Show extra debugging information" @@ -593,6 +805,24 @@ msgid "Show plugin verbose information" msgstr "Show plugin verbose information" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Show the debug log from the last attempted update" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Show the information of firmware update status" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Shutdown now?" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Specify Vendor/Product ID(s) of DFU device" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Specify the number of bytes per USB transfer" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "State" @@ -610,11 +840,16 @@ 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." +#. 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 "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." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "This program may only work correctly as 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 "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." +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -628,6 +863,10 @@ msgid "Type" msgstr "Type" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI Firmware Utility" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -643,6 +882,10 @@ msgid "Unlocks the device for firmware access" msgstr "Unlocks the device for firmware access" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Unset the debugging flag during update" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Update Checksum" @@ -651,6 +894,11 @@ msgid "Update Description" msgstr "Update Description" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Update Duration" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Update Location" @@ -671,8 +919,13 @@ msgid "Update Version" msgstr "Update Version" -msgid "Update device firmware on Linux" -msgstr "Update device firmware on Linux" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Update all devices that match local metadata" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Update failure is a known issue, visit this URL for more information:" #. TRANSLATORS: ask the user if we can update the metadata msgid "Update now?" @@ -696,10 +949,23 @@ msgid "Updating %s from %s to %s... " msgstr "Updating %s from %s to %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Updating %s…" + +#. TRANSLATORS: the server sent the user a small message +msgid "Upload message:" +msgstr "Upload message:" + #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "Upload report now?" +#. 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 "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Username" @@ -721,6 +987,10 @@ msgstr "Watch DFU devices being hotplugged" #. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Watch for hardware changes" + +#. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "Write firmware from file into device" @@ -732,5 +1002,6 @@ msgid "Writing…" msgstr "Writing…" -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 "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." diff -Nru fwupd-1.0.9/po/eo.po fwupd-1.2.10/po/eo.po --- fwupd-1.0.9/po/eo.po 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/po/eo.po 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,95 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# kristjan , 2019 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Esperanto (http://www.transifex.com/freedesktop/fwupd/language/eo/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: eo\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekundo" +msgstr[1] "%u sekundoj" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Aldonita" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Nuligi" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Nuligita" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Ŝanĝita" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Elektu aparaton:" + +#. TRANSLATORS: section header for firmware description +msgid "Description" +msgstr "Priskribo" + +#. success +msgid "Done!" +msgstr "Farita!" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Trovita" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "ID" + +msgid "Mode" +msgstr "Reĝimo" + +#. TRANSLATORS: interface name, e.g. "Flash" +#. TRANSLATORS: device name, e.g. 'ColorHug2' +#. TRANSLATORS: section header for the release name +msgid "Name" +msgstr "Nomo" + +msgid "OK" +msgstr "Bone" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Protokolo" + +#. TRANSLATORS: these are areas of memory on the chip +msgid "Region" +msgstr "Regiono" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Forigita" + +msgid "Target" +msgstr "Celo" + +#. TRANSLATORS: currect daemon status is unknown +msgid "Unknown" +msgstr "Nekonata" + +#. TRANSLATORS: section header for release version number +msgid "Version" +msgstr "Versio" diff -Nru fwupd-1.0.9/po/eu.po fwupd-1.2.10/po/eu.po --- fwupd-1.0.9/po/eu.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/eu.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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 -Nru fwupd-1.0.9/po/fi.po fwupd-1.2.10/po/fi.po --- fwupd-1.0.9/po/fi.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/fi.po 2019-07-15 18:25:54.000000000 +0000 @@ -4,13 +4,11 @@ # # Translators: # Jiri Grönroos , 2017-2018 +# Kimmo Kujansuu , 2019 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: Finnish (http://www.transifex.com/freedesktop/fwupd/language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,15 +16,214 @@ "Language: fi\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 minuuttia jäljellä" +msgstr[1] "%.0f minuuttia jäljellä" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%sKuluttajan ME-päivitys" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%sOhjaimen päivitys" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%sYrityksen ME-päivitys" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%sLaitteen päivitys" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%sSulautetun ohjaimen päivitys" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%sME päivitys" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%sJärjestelmän päivitys" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%sPäivitys" + +#. TRANSLATORS: first replacement is device name +#, c-format +msgid "%s has firmware updates:" +msgstr "%s on laiteohjelmistopäivityksiä:" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%upäivää" +msgstr[1] "%upäivää" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u tuntia" +msgstr[1] "%utuntia" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuuttia" +msgstr[1] "%u minuuttia" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u " +msgstr[1] "%u sekunttia" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktivoi laitteet" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktivoi odottavat laitteet" + +msgid "Activate the new firmware on the device" +msgstr "Aktivoi laitteessa oleva uusi laiteohjelmisto 'firmware'" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Firmware-päivityksen aktivointi" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Käynnistä laiteohjelmiston päivitys" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Lisätty" +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Ikä" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Hyväksy ja ota etäyhteys käyttöön?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Peitenimi %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Salli firmware-versioiden alentaminen" + +#. TRANSLATORS: command line option +msgid "Allow re-installing existing firmware versions" +msgstr "Salli laiteohjelmiston versioiden asentaminen uudelleen" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Päivitys vaatii uudelleenkäynnistyksen." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Päivitys edellyttää järjestelmän sammuttamista." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Vastaa kaikkiin kysymyksiin kyllä" + +#. TRANSLATORS: command description +msgid "Apply a binary patch" +msgstr "Käytä binääristä korjaustiedostoa" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Käytä firmware-päivityksiä" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Hyväksytty laiteohjelmisto:" +msgstr[1] "Hyväksytty laiteohjelmisto:" + +#. TRANSLATORS: command description +msgid "Attach DFU capable device back to runtime" +msgstr "Kiinnitä DFU-kykyinen laite takaisin ajoon" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Kiinnitä laiteohjelmistotilaan" + +#. TRANSLATORS: device attributes, i.e. things that +#. * the device can do +msgid "Attributes" +msgstr "Määritteet" + #. TRANSLATORS: waiting for user to authenticate msgid "Authenticating…" msgstr "Tunnistaudutaan…" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Todennus on tarpeen, jotta firmware voidaan alentaa siirrettävällä laitteelta" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Todennus on tarpeen tämän laitteen firmwaren alentamiseksi" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Todennusta tarvitaan firmware-päivityksiin käytettävän konfiguroidun etä-ohjaimen muokkaamisessa" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Todennusta tarvitaan taustaprosessin asetusten muokkaamiseen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Vahvistus on tarpeen hyväksyttyjen laiteohjelmien listan määrittämiseksi" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Autentikointi edellyttää tietojen allekirjoittamista asiakaan sertifikaatin avulla" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Uuden firmware-version käyttöönottoon tarvitaan todennus" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Laitteen lukituksen avaaminen vaatii tunnistautumisen" @@ -38,6 +235,18 @@ msgid "Authentication is required to update the firmware on this machine" msgstr "Tämän laitteen firmwaren päivittäminen vaatii tunnistautumisen" +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Todennus edellyttää laitteen tallennettujen tarkistussummien päivittämistä" + +#. TRANSLATORS: command description +msgid "Build firmware using a sandbox" +msgstr "Rakenna laiteohjelmisto hiekkalaatikon avulla" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Peru" + #. TRANSLATORS: this is when a device ctrl+c's a watch msgid "Cancelled" msgstr "Peruttu" @@ -52,6 +261,10 @@ msgid "Checksum" msgstr "Tarkistussumma" +#. TRANSLATORS: chip ID, e.g. "0x58200204" +msgid "Chip ID" +msgstr "Chip-tunnus" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Valitse laite:" @@ -60,10 +273,37 @@ msgid "Choose a release:" msgstr "Valitse julkaisu:" +#. TRANSLATORS: this is the encryption method used when writing +msgid "Cipher" +msgstr "Salaus" + +#. TRANSLATORS: command description +msgid "Clears any updates scheduled to be updated offline" +msgstr "Tyhjentää päivitykset, jotka on tarkoitus päivittää offline-tilassa" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Tyhjennä viimeisimmän päivityksen tulokset" + #. TRANSLATORS: error message msgid "Command not found" msgstr "Komentoa ei löytynyt" +#. TRANSLATORS: command description +msgid "Convert firmware to DFU format" +msgstr "Muunna firmware DFU-muotoon" + +#. TRANSLATORS: command description +msgid "Create a binary patch using two files" +msgstr "Luo binäärinen korjaustiedosto käyttäen kahta tiedostoa" + +msgid "DFU" +msgstr "DFU" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU-apuohjelma" + #. TRANSLATORS: for the --verbose arg msgid "Debugging Options" msgstr "Vianjäljitysvalinnat" @@ -72,10 +312,26 @@ msgid "Decompressing…" msgstr "Puretaan…" +#. TRANSLATORS: command description +msgid "Decrypt firmware data" +msgstr "Pura firmwaren data" + #. TRANSLATORS: section header for firmware description msgid "Description" msgstr "Kuvaus" +#. TRANSLATORS: command description +msgid "Detach currently attached DFU capable device" +msgstr "Irrota tällä hetkellä liitetty DFU-laite" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Irrota käynnistyslataimen tilaan" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Laitteen tunnus" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Laite lisätty:" @@ -88,13 +344,115 @@ msgid "Device removed:" msgstr "Laite poistettu:" +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Laitteet, jotka on päivitetty onnistuneesti:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Laitteet, joita ei päivitetty oikein:" + +msgid "Disabled fwupdate debugging" +msgstr "fw-päivityksen virheenkorjaus poistettu" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Poista annettu etäyhteys" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Näytä versio" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Älä tarkista vanhoja metatietoja" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Älä tarkista käynnistystä uudelleen päivityksen jälkeen" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Älä tarkista ilmoittamattomasta historiasta" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Älä kirjoita historiatietokantaan" + +#. success msgid "Done!" msgstr "Valmis!" +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Alentaa laitteen laiteohjelmistoa" + +#. 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 "Alenee %s -sta %s tulos %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Alentaa %s…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Ladataan…" +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Pura SMBIOS-tiedot tiedostosta" + +#. TRANSLATORS: command description +msgid "Dump details about a firmware file" +msgstr "Tyhjennä laiteohjelmiston tiedot" + +#. TRANSLATORS: command description +msgid "Dump information about a binary patch to the screen" +msgstr "Tulosta tiedot binäärisestä korjaustiedostosta näytölle" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Määritetty ESP ei ollut voimassa" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Ota firmware-päivitystuki käyttöön tuetuissa järjestelmissä" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Ota etäyhteys käyttöön?" + +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Käytössä" + +msgid "Enabled fwupdate debugging" +msgstr "fw-päivityksen virheenkorjaus käytössä" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Ota käyttöön annettu etäyhteys" + +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 "Tämän toiminnon käyttöönotto tapahtuu omalla vastuullasi, joten sinun on otettava yhteyttä alkuperäiseen laitevalmistajaan näiden päivitysten aiheuttamista ongelmista. Vain päivitysprosessiin liittyvät ongelmat pitäisi jättää osoitteeseen $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Tämän etä-ohjaimen käyttöönotto tapahtuu omalla vastuullasi." + +#. TRANSLATORS: command description +msgid "Encrypt firmware data" +msgstr "Salaa firmwaren data" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Poista kaikki laiteohjelmiston päivityshistoria" + #. TRANSLATORS: erasing contents of the flash chips msgid "Erasing…" msgstr "Poistetaan…" @@ -103,6 +461,42 @@ msgid "Exit after a small delay" msgstr "Poistu pienen viiveen jälkeen" +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Poistu kun moottori on ladattu" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Yhteys taustaprosessiin epäonnistui" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Lataaminen epäonnistui palvelimen rajoituksen vuoksi" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Vireillä olevien laitteiden saaminen epäonnistui" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Laiteohjelmiston firmware päivityksen asennus epäonnistui" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Quirksin lataaminen epäonnistui" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Argumenttien jäsentäminen epäonnistui" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Käynnistys epäonnistui" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Splash-tilan asettaminen epäonnistui" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Noudetaan tiedosto" @@ -123,65 +517,675 @@ msgid "Filename" msgstr "Tiedostonimi" +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Tiedoston allekirjoitus" + +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Firmware-agentti" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Firmware pohja URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Laiteohjelmiston päivitys D-Bus-palveluun" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Firmware-päivityksen taustaprosessi" + #. TRANSLATORS: program name msgid "Firmware Utility" msgstr "Firmware-työkalu" +#. 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] "Laiteohjelmiston metatietoja ei ole päivitetty %upäivään ja ne eivät välttämättä ole ajan tasalla." +msgstr[1] "Laiteohjelmiston metatietoja ei ole päivitetty %upäivään ja ne eivät välttämättä ole ajan tasalla." + +msgid "Firmware updates are not supported on this machine." +msgstr "Ohjelmistopäivitys 'firmware' ei tue tätä laitetta" + +msgid "Firmware updates are supported on this machine." +msgstr "Ohjelmistopäivitys 'firmware' tukee tätä laitetta" + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Liput" + +msgid "Force the action ignoring all warnings" +msgstr "Pakota toimenpide huomioimatta kaikki varoitukset" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Löydetty" + +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Hae kaikki laitteet järjestelmän topologian mukaisesti" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Hanki kaikki laitteet ja mahdolliset julkaisut" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Hae kaikki laiteohjelmistopäivityksiä tukevat laitteet" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Hanki kaikki järjestelmään rekisteröidyt laajennukset" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Hae tietoja firmware-tiedostosta" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Määrittää konfiguroidut etäyhteydet" + +#. TRANSLATORS: command description +msgid "Gets the cryptographic hash of the dumped firmware" +msgstr "Hae salakirjoituksen kohteena oleva laiteohjelmisto." + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Hae hyväksytty laiteohjelmiston luettelo." + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Antaa luettelon liitettyjen laitteiden päivityksistä" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Antaa laitteen julkaisut" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Saat viimeisimmän päivityksen tulokset" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "Henkilöllisyys" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Jouten…" +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Asenna laiteohjelmiston merkintä laitteeseen" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Asenna laiteohjelmisto tähän laitteistoon" + +msgid "Install old version of system firmware" +msgstr "Asenna vanha firmware versio" + +msgid "Install signed device firmware" +msgstr "Asenna allekirjoitettu laitteen firmware" + +msgid "Install signed system firmware" +msgstr "Asenna allekirjoitettu järjestelmän firmware" + +msgid "Install unsigned device firmware" +msgstr "Asenna allekirjoittamattoman laitteen firmware" + +msgid "Install unsigned system firmware" +msgstr "Asenna allekirjoittamaton järjestelmän firmware" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Asentaa firmwarea…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Asennetaan firmware-päivitystä…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Asentaa %s…" + +msgid "Keyring" +msgstr "Avaimet" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Jäljellä on alle minuutti" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux-toimittajan laiteohjelmisto (vakaa firmware)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux-toimittajan laiteohjelmisto (firmware testaus)" + +#. TRANSLATORS: command description +msgid "List currently attached DFU capable devices" +msgstr "Luettelo tällä hetkellä liitetyistä DFU-laitteista" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Lista tuetuista firmware-päivityksistä" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Ladataan…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Luetteloi tiettyjä lisäosia manuaalisesti" + +#. TRANSLATORS: command description +msgid "Merge multiple firmware files into one" +msgstr "Yhdistä useita firmware-tiedostoja yhteen" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metatietojen URI" + +#. TRANSLATORS: remote URI +msgid "Metadata URI Signature" +msgstr "Metatietojen URI-allekirjoitus" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metatiedot voidaan hankkia Linux Vendor Firmware -palvelusta." + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Virheellinen taustaprosessi ja asiakas, käytä sen sijaan %s" + +msgid "Mode" +msgstr "Malli" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Muuta taustaprosessin määritysarvoja." + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Muuta annettua etäyhteyttä" + +msgid "Modify a configured remote" +msgstr "Muokkaa määritettyä etänä" + +msgid "Modify daemon configuration" +msgstr "Muokkaa taustaprosessin kokoonpanoa" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Seuraa tapahtumien taustaprosessia" + #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' #. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nimi" +msgid "No action specified!" +msgstr "Toimintoa ei ole määritetty!" + +#. TRANSLATORS: nothing attached +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Ei havaittu sopivaa laitteistoa firmware päivitykselle" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Ei laajennuksia" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Mitään etäyhteyksiä ei ole tällä hetkellä käytössä, joten metatietoja ei ole saatavilla." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Ei soveltuvia päivityksiä" + msgid "OK" msgstr "OK" +#. TRANSLATORS: command line option +msgid "Override plugin warning" +msgstr "Ohita plugin-varoitus" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Ohita oletusarvoinen ESP-polku" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Ohita varoitukset ja pakota toiminto" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Salasana" +msgid "Payload" +msgstr "Tietosisältö" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Prosenttiosuus valmis" + +msgid "Permission denied" +msgstr "Lupa kielletty" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Anna numero 0 -%u" + +msgid "Print the version number" +msgstr "Tulosta versionumero" + +msgid "Print verbose debug statements" +msgstr "Tulosta virheelliset virheilmoitukset" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Prioriteetti" + +msgid "Proceed with upload?" +msgstr "Jatka lähettämistä?" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Protokolla" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Kysely firmware-päivityksen tuesta" + +#. TRANSLATORS: device quirks, i.e. things that +#. * it does that we have to work around +msgid "Quirks" +msgstr "Merkintä" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Lue firmware laitteesta tiedostoon" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Lue firmware osiolta tiedostoon" + #. TRANSLATORS: reading from the flash chips msgid "Reading…" msgstr "Luetaan…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Käynnistää..." + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Päivitä etäpalvelimen metatiedot" + +#. TRANSLATORS: these are areas of memory on the chip +msgid "Region" +msgstr "Alue" + +#. 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 "Uudelleenasennus %s kera %s... " + +#. TRANSLATORS: section header for the remote the file is coming from +msgid "Remote" +msgstr "Etä" + +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Etätunnus" + #. TRANSLATORS: this is when a device is hotplugged msgid "Removed" msgstr "Poistettu" +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Vaihda tiedot olemassa olevaan firmware-tiedostoon" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Ilmoita URI" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Vaatii Internet-yhteyden" + +#. TRANSLATORS: command description +msgid "Reset a DFU device" +msgstr "Nollaa DFU-laite" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Käynnistä nyt?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Käynnistä taustaprosessi uudelleen, jotta muutos tulee voimaan" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Käynnistetään laite uudelleen…" +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Palauta kaikki laitteen laitteistotunnukset" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Suorita plugin-puhdistustoiminto, kun käytät asennus-blobia" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Asennus-blobia ajamalla käytät yhdistelmän valmiita laajennus rutiineja " + +msgid "Runtime" +msgstr "Käyttöaika" + +#. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Tallenna laitteen tila JSON-tiedostoon suoritusten välillä" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Ajasta asennus uudelleenkäynnistykselle mahdollisuuksien mukaan" + #. TRANSLATORS: scheduing an update to be done on the next boot msgid "Scheduling…" msgstr "Ajoitetaan…" +#. TRANSLATORS: serial number, e.g. '00012345' +msgid "Serial" +msgstr "Sarjanumero" + +#. TRANSLATORS: command description +msgid "Set alternative name on firmware file" +msgstr "Aseta vaihtoehtoinen nimi firmware-tiedostolle" + +#. TRANSLATORS: command description +msgid "Set alternative number on firmware file" +msgstr "Aseta vaihtoehtoinen numero firmware-tiedostolle" + +#. TRANSLATORS: command description +msgid "Set element address on firmware file" +msgstr "Aseta elementin osoite firmware tiedostoon" + +#. TRANSLATORS: command description +msgid "Set product ID on firmware file" +msgstr "Aseta tuotetunnus firmware tiedostoon" + +#. TRANSLATORS: command description +msgid "Set release version on firmware file" +msgstr "Aseta julkaisuversio firmware-tiedostosta" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Aseta virheenkorjauksen lippu päivityksen aikana" + +#. TRANSLATORS: command description +msgid "Set the firmware size for the target" +msgstr "Määritä firmwaren kohteen koko" + +#. TRANSLATORS: command description +msgid "Set vendor ID on firmware file" +msgstr "Aseta toimittajan tunnus firmware tiedostoon" + +#. TRANSLATORS: command description +msgid "Sets metadata on a firmware file" +msgstr "Asetta metatiedot firmware -tiedostoon" + +msgid "Sets the list of approved firmware" +msgstr "Asettaa listan hyväksytyjä 'firmware' laiteohjelmistoja " + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Asettaa hyväksytyn laiteohjelmiston luettelon." + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Jaa laiteohjelmiston historia kehittäjien kanssa" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Näytä asiakas- ja taustaprosessin versiot" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Näytä taustaprosessin täydelliset tiedot tietylle verkkotunnukselle" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Näytä kaikkien verkkotunnusten virheenkorjaustiedot" + #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Näytä vianjäljitysvalinnat" +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Näytä laitteet, joita ei voi päivittää" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Näytä ylimääräiset virheenkorjaustiedot" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Näytä laiteohjelmiston päivitysten historia" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Näytä plugin tiedot" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Näytä virheenkorjausloki viimeisestä päivitysyrityksestä" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Näytä tiedot laiteohjelmiston 'firmware' päivitystilasta" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Sammuta nyt?" + +msgid "Sign data using the client certificate" +msgstr "Allekirjoita tietoja käyttämällä asiakkaan sertifikaattia" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Allekirjoita tietoja käyttämällä asiakkaan sertifikaattia" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Kirjoita ladatut tiedot asiakaan sertifikaatilla" + +msgid "Signature" +msgstr "Allekirjoitus" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Määritä DFU laitteen toimittaja/tuotetunnus(s)" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Määritä tavujen määrä USB-siirtoa kohti" + +#. TRANSLATORS: device state, i.e. appIDLE +msgid "State" +msgstr "Osavaltio" + +#. TRANSLATORS: probably not run as root... +#. TRANSLATORS: device has failed to report status +#. TRANSLATORS: device status, e.g. "OK" +msgid "Status" +msgstr "Tila" + +#. TRANSLATORS: section header for the release one line summary +msgid "Summary" +msgstr "Yhteenveto" + +msgid "Target" +msgstr "Kohde" + +#. 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 on ilmainen palvelu, joka toimii itsenäisenä oikeushenkilönä eikä sillä ole yhteyttä $OS_RELEASE:NAME$. Jakelijasi ei ehkä ole tarkistanut mitään laiteohjelmistopäivityksiä, jotka ovat yhteensopivia järjestelmän tai liitettyjen laitteiden kanssa. Kaikki laiteohjelmistot on tarkoitettu vain alkuperäisen laitteen valmistajalle." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Hyväksyttyä laiteohjelmistoa ei ole." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Tämä ohjelma voi toimia vain juuressa '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 "Tämä sisältää firmwaren, jota ei ole vientikiellossa, mutta jota laitteistotoimittaja testaa edelleen. Varmista, että voit päivittää firmwaren manuaalisesti, jos päivitys epäonnistuu." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Tätä työkalua voi käyttää vain root-käyttäjä" + +#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" +msgid "Title" +msgstr "Otsikko" + +#. TRANSLATORS: transfer size in bytes +msgid "Transfer Size" +msgstr "Siirron koko" + #. TRANSLATORS: remote type, e.g. remote or local msgid "Type" msgstr "Tyyppi" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI firmware -apuohjelma" + +#. TRANSLATORS: section header for firmware URI +msgid "URI" +msgstr "URI" + #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Tuntematon" -msgid "Update device firmware on Linux" -msgstr "Päivitä laitteiden firmware-laiteohjelmistoja Linuxilla" +msgid "Unlock the device to allow access" +msgstr "Sallia pääsy laitteeseen" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Avaa pääsy laitteen laiteohjelmistoon" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Poista virheenkorjauksen lippu päivityksen aikana" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Ei tuettu taustaprosessin versio %s, asiakasversio on %s" + +#. TRANSLATORS: section header for firmware checksum +msgid "Update Checksum" +msgstr "Päivitä tarkistussumma" + +#. TRANSLATORS: section header for long firmware desc +msgid "Update Description" +msgstr "Päivitä kuvaus" + +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Päivitä kesto" + +#. TRANSLATORS: section header for firmware remote http:// +msgid "Update Location" +msgstr "Päivitä sijainti" + +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "Päivitä nimi" + +#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing +msgid "Update Remote ID" +msgstr "Päivitä etätunnus" + +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "Päivitä yhteenveto" + +#. TRANSLATORS: section header for firmware version +msgid "Update Version" +msgstr "Päivitä versio" + +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Päivitä kaikki paikallisia metatietoja vastaavat laitteet" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Päivityksen epäonnistuminen on tunnettu ongelma. Saat lisätietoja tästä URL-osoitteesta:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Päivitä nyt?" + +msgid "Update the stored device verification information" +msgstr "Päivitä laitteen tallennetut vahvistustiedot" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current ROM contents" +msgstr "Päivitä tallennetut metatiedot nykyisen ROM-sisällön kanssa" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Päivitä tallennetut metatiedot nykyiseen sisältöön" + +#. TRANSLATORS: command description +msgid "Updates all firmware to latest versions available" +msgstr "Päivittää kaikki laiteohjaimet uusimpiin saatavilla oleviin versioihin" + +#. 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 "Korotus %s -sta %s tulos %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Päivittää %s…" + +#. TRANSLATORS: the server sent the user a small message +msgid "Upload message:" +msgstr "Lähetä viesti:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Lataa raportti nyt?" + +#. 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 "Firmware-raporttien lataaminen auttaa laitteistotoimittajia tunnistamaan nopeasti virheelliset ja onnistuneet päivitykset todellisissa laitteissa." #. TRANSLATORS: remote filename base msgid "Username" @@ -199,9 +1203,26 @@ msgid "Waiting…" msgstr "Odotetaan…" +#. TRANSLATORS: command description +msgid "Watch DFU devices being hotplugged" +msgstr "Katso, että DFU-laitteet on kytketty" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Katso laitteiston muutoksia" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Kirjoita firmware tiedostosta laitteeseen" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Kirjoita firmware tiedostosta osioon" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Kirjoitetaan…" -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 "Jakelijasi ei ehkä ole tarkistanut mitään laiteohjelmistopäivityksiä, jotka ovat yhteensopivia järjestelmän tai liitettyjen laitteiden kanssa." diff -Nru fwupd-1.0.9/po/fr.po fwupd-1.2.10/po/fr.po --- fwupd-1.0.9/po/fr.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/fr.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: French (http://www.transifex.com/freedesktop/fwupd/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -31,6 +28,7 @@ msgid "Debugging Options" msgstr "Options de débogage" +#. success msgid "Done!" msgstr "Terminé !" @@ -69,10 +67,6 @@ msgid "Install a firmware file on this hardware" msgstr "Installer un fichier de micrologiciel sur ce matériel" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Installer immédiatement les mises à jour préparées" - #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" @@ -85,10 +79,6 @@ msgid "Reinstalling %s with %s... " msgstr "Réinstallation de %s en %s" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Montrer les informations de débogage pour tous les fichiers" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Montrer les options de débogage" diff -Nru fwupd-1.0.9/po/fur.po fwupd-1.2.10/po/fur.po --- fwupd-1.0.9/po/fur.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/fur.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: Friulian (http://www.transifex.com/freedesktop/fwupd/language/fur/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -44,7 +41,7 @@ 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î." @@ -183,6 +180,7 @@ msgid "Devices that were not updated correctly:" msgstr "Dispositîfs che no son stâts inzornâts ben:" +#. success msgid "Done!" msgstr "Fat!" @@ -322,10 +320,6 @@ msgid "Install old version of system firmware" msgstr "Instale une version vecje dal firmware di sisteme" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Instale inzornaments preparâts cumò" - msgid "Install signed device firmware" msgstr "Instasle firmware di dispositîf firmât" @@ -528,10 +522,6 @@ msgid "Show client and daemon versions" msgstr "Mostre versions di client e demoni" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Mostre lis informazions di debug par ducj i file" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Mostre opzions di debug" @@ -562,9 +552,6 @@ 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" @@ -616,9 +603,6 @@ 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:" @@ -672,6 +656,3 @@ #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Daûr a scrivi…" - -msgid "fwupd" -msgstr "fwupd" diff -Nru fwupd-1.0.9/po/he.po fwupd-1.2.10/po/he.po --- fwupd-1.0.9/po/he.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/he.po 2019-07-15 18:25:54.000000000 +0000 @@ -9,9 +9,6 @@ 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: Hebrew (http://www.transifex.com/freedesktop/fwupd/language/he/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -50,6 +47,7 @@ msgid "Description" msgstr "תיאור" +#. success msgid "Done!" msgstr "הסתיים!" @@ -92,10 +90,6 @@ msgid "Install a firmware file on this hardware" msgstr "מתקין קובץ קושחה בחומרה זו" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "מתקין כעת עדכונים מוכנים" - #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" @@ -111,10 +105,6 @@ msgid "Reinstalling %s with %s... " msgstr "מתקין מחדש %s עם %s..." -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "הצג מידע ניפוי שגיאות לכל הקבצים" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "הצג אפשרויות ניפוי שגיאות" diff -Nru fwupd-1.0.9/po/hi.po fwupd-1.2.10/po/hi.po --- fwupd-1.0.9/po/hi.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/hi.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: Hindi (http://www.transifex.com/freedesktop/fwupd/language/hi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -31,6 +28,7 @@ msgid "Debugging Options" msgstr "डिबगिंग के विकल्प " +#. success msgid "Done!" msgstr "हो गया !" @@ -69,10 +67,6 @@ msgid "Install a firmware file on this hardware" msgstr "फर्मवेयर फाइल को इस हार्डवेयर पर स्थापित करें " -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "तैयार अपडेट अभी स्थापित करें " - #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" @@ -85,10 +79,6 @@ msgid "Reinstalling %s with %s... " msgstr "%s को %s से दोबारा स्थापित करा जा रहा है " -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "फाइल्स की डिबगिंग की जानकारी दिखाए " - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "डिबगिंग के विकल्प दिखाए " diff -Nru fwupd-1.0.9/po/hr.po fwupd-1.2.10/po/hr.po --- fwupd-1.0.9/po/hr.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/hr.po 2019-07-15 18:25:54.000000000 +0000 @@ -5,14 +5,11 @@ # Translators: # FIRST AUTHOR , 2016 # gogo , 2016 -# gogo , 2016-2018 +# gogo , 2016-2019 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: Croatian (http://www.transifex.com/freedesktop/fwupd/language/hr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,11 +17,121 @@ "Language: hr\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minuta preostala" +msgstr[1] "%.0f minute preostale" +msgstr[2] "%.0f minuta preostalo" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s Potrošački pogon upravljanja (ME) nadopunjen" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s nadopuna upravljača" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s Korporativni pogon upravljanja (ME) nadopunjen" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s uređaj nadopunjen" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s ugrađeni upravljač nadopunjen" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s Pogon upravljanja (ME) nadopunjen" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s sustav nadopunjen" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s nadopuna" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s ima nadopune frimvera:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dan" +msgstr[1] "%u dana" +msgstr[2] "%u dana" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u sat" +msgstr[1] "%u sata" +msgstr[2] "%u sati" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuta" +msgstr[1] "%u minute" +msgstr[2] "%u minuta" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekunda" +msgstr[1] "%u sekunde" +msgstr[2] "%u sekundi" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktiviraj uređaj" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktiviraj uređaje na čekanju" + +msgid "Activate the new firmware on the device" +msgstr "Aktiviraj novi frimver na uređaju" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktivacija nadopune frimvera" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktivacija nadopune frimvera za" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Dodano" @@ -33,6 +140,10 @@ msgid "Age" msgstr "Dob" +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Slažete se s omogućavanjem udaljene lokacije?" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" @@ -46,10 +157,14 @@ 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." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Nadopuna zahtijeva potpuno isključivanje sustava." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Odgovori 'da' na sva pitanja" @@ -58,10 +173,26 @@ msgid "Apply a binary patch" msgstr "Primijeni binarnu zakrpu" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Primijeni nadopune frimvera" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Odobreni frimver:" +msgstr[1] "Odobreni frimveri:" +msgstr[2] "Odobreni frimveri:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Poveži DFU sposoban uređaj natrag u vremenu izvršavanja" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Prebaci u način frimvera" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -84,6 +215,22 @@ msgstr "Potrebna je ovjera za promjenu udaljene lokacije koja se koristi za nadopunu frimvera" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Potrebna je ovjera za promjenu podešavanja pozadinskog programa" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Potrebna je ovjera za postavljanje popisa odobrenih frimvera" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Potrebna je ovjera za potpisivanje podataka vjerodajnicom klijenta" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Potrebna je ovjera za prebacivanje na novu inačicu frimvera" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Potrebna je ovjera za otključavanje uređaja" @@ -184,6 +331,14 @@ msgid "Detach currently attached DFU capable device" msgstr "Odspoji trenutno povezane DFU sposobne uređaje" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Prebaci u način učitača pokretanja" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "ID uređaja" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Uređaj dodan:" @@ -204,6 +359,17 @@ msgid "Devices that were not updated correctly:" msgstr "Uređaji koji nisu ispravno nadopunjeni:" +msgid "Disabled fwupdate debugging" +msgstr "Onemogući fwupdate otklanjanje grešaka" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Onemogućuje zadane udaljene lokacije" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Prikaži inačicu" + #. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Ne provjeravaj stare metapodatke" @@ -216,6 +382,11 @@ msgid "Do not check for unreported history" msgstr "Ne provjeravaj neprijavljenu povijest" +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Ne zapisuj bazu podataka povijesti" + +#. success msgid "Done!" msgstr "Završeno!" @@ -230,6 +401,11 @@ msgid "Downgrading %s from %s to %s... " msgstr "Vraćanje %s s inačice %s na inačicu %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Vraćanje %s na stariju inačicu…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Preuzimanje..." @@ -246,10 +422,36 @@ msgid "Dump information about a binary patch to the screen" msgstr "Ispiši informacije o binarnoj zakrpi na zaslonu" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Određeni ESP nije ispravan" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Omogući podršku nadopune frimvera na podržanim sustavima" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Omogući ovu udaljenu lokaciju?" + #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Omogućeno" +msgid "Enabled fwupdate debugging" +msgstr "Omogući fwupdate otklanjanje grešaka" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Omogućuje zadane udaljene lokacije" + +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "Omogućujete ovu funkcionalnost na vlastiti rizik, što znači da morate kontaktirati svog izvornog proizvođača opreme u vezi problema uzrokovanih tim nadopunama. Samo probleme sa samim postupkom nadopune treba prijaviti na $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Ovu udaljenu lokaciju omogućavate na vlastiti rizik." + #. TRANSLATORS: command description msgid "Encrypt firmware data" msgstr "Šifriraj podatke frimvera" @@ -270,6 +472,22 @@ msgid "Exit after the engine has loaded" msgstr "Izađi nakon učitavanja pogona" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Neuspjelo povezivanje s pozadinskim programom" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Neuspjelo preuzimanje zbog ograničenja poslužitelja" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Neuspjeli prikaz uređaja na čekanju" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Neuspjela instalacija nadopune frimvera" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Neuspjelo učitavanje okolnosti uređaja" @@ -278,6 +496,10 @@ msgid "Failed to parse arguments" msgstr "Neuspjela obrada argumenata" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Neuspjelo ponovno pokretanje" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Dohvaćanje datoteke" @@ -302,6 +524,10 @@ msgid "Filename Signature" msgstr "Potpis naziva datoteke" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Firmver agent" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "Osnovni URI frimvera" @@ -326,6 +552,19 @@ msgstr[1] "Metapodaci firmvera nisu nadopunjeni %u dana i možda nisu najnoviji." msgstr[2] "Metapodaci firmvera nisu nadopunjeni %u dana i možda nisu najnoviji." +msgid "Firmware updates are not supported on this machine." +msgstr "Nadopuna frimvera nije podržana na ovom računalu." + +msgid "Firmware updates are supported on this machine." +msgstr "Nadopuna frimvera je podržana na ovom računalu." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Oznake" + +msgid "Force the action ignoring all warnings" +msgstr "Prisili radnju zanemarivanja svih upozorenja" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Pronađen" @@ -334,10 +573,22 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Prikaži sve uređaje prema topologiji sustava" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Prikaži sve uređaje i moguća izdanja" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Prikaži sve uređaje koji podržavaju nadopunu frimvera" #. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Prikaži sve omogućene priključke registrirane sa sustavom" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "Prikaži pojedinosti datoteke frimvera" @@ -349,6 +600,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Prikaži kriptografsku jedinstvenu vrijednost opširnijih informacija frimvera" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Prikazuje popis odobrenih frimvera." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Prikaži popis nadopuna za povezani hardver" @@ -370,16 +625,16 @@ msgstr "Mirovanje..." #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instaliraj blob frimvera na uređaj" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Instaliraj datoteku frimvera na ovaj uređaj" msgid "Install old version of system firmware" msgstr "Instaliraj stariju inačicu frimvera sustava" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Instaliraj pripremljene uređaje odmah" - msgid "Install signed device firmware" msgstr "Instaliraj frimver potpisan uređajem" @@ -392,21 +647,48 @@ msgid "Install unsigned system firmware" msgstr "Instaliraj frimver nepotpisan sustavom" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalacija frimvera…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Instalacija nadopune frimvera..." +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instaliram na %s…" + msgid "Keyring" msgstr "Skup ključeva" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Preostalo je manje od minute" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Frimver Usluga Linux Proizvođača (LVFS) (stabilni frimver)." + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Frimver Usluga Linux Proizvođača (LVFS) (testni frimver)." + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "Prikaži trenutno povezane DFU sposobne uređaje" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Prikaži nadopune podržanih frimvera" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Učitavanje..." +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Ručno dopusti određene priključke" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "Spoji više frimver datoteka u jednu" @@ -419,9 +701,22 @@ msgid "Metadata URI Signature" msgstr "Potpis URI metapodataka" +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metapodaci se mogu dobiti od Frimver Usluge Linux Proizvođača (LVFS)." + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Pozadinski program i klijent se ne podudaraju, umjesto koristite %s" + msgid "Mode" msgstr "Način" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Prilagođava vrijednost podešavanja pozadinskog programa." + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Promjena zadane udaljene lokacije" @@ -429,6 +724,9 @@ msgid "Modify a configured remote" msgstr "Promijeni zadanu udaljenu lokaciju" +msgid "Modify daemon configuration" +msgstr "Prilagodi podešavanje pozadinskog programa" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Nadgledaj događaje pozadinskim programom" @@ -439,11 +737,26 @@ msgid "Name" msgstr "Naziv" +msgid "No action specified!" +msgstr "Nema zadane radnje!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nema otkrivenog hardvera s mogućnosti nadopune frimvera" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nema pronađenih priključaka" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Trenutno nema omogućenih udaljenih lokacija stoga nema dostupnih metapodataka." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nema primijenjenih nadopuna" + msgid "OK" msgstr "U redu" @@ -451,10 +764,22 @@ msgid "Override plugin warning" msgstr "Zaobiđi upozorenja priključka" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Zaobiđi zadanu ESP putanju" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Zaobiđi upozorenja i prisili radnju" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Lozinka" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Postotak završetka" + msgid "Permission denied" msgstr "Dozvola odbijena" @@ -463,6 +788,12 @@ msgid "Please enter a number from 0 to %u: " msgstr "Odaberite broj od 0 do %u: " +msgid "Print the version number" +msgstr "Prikaži broj inačice" + +msgid "Print verbose debug statements" +msgstr "Zapisuj opširniji izvještaj otklanjanja grešaka" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Prioritet" @@ -474,6 +805,10 @@ msgid "Protocol" msgstr "Protokol" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Zatraži podršku nadopune frimvera" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -491,6 +826,10 @@ msgid "Reading…" msgstr "Čitanje..." +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Ponovno pokretanje…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Osvježi metapodatke s udaljenog poslužitelja" @@ -538,6 +877,10 @@ msgid "Restart now?" msgstr "Ponovno pokreni odmah?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Ponovno pokreni pozadinski program kako bi se promjene primijenile?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Ponovno pokretanje uređaja..." @@ -546,10 +889,22 @@ msgid "Return all the hardware IDs for the machine" msgstr "Vrati sve ID-ove hardvera za uređaj" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Pokreni rutinu čišćenja sastavljanja priključka kada se koristi install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Pokreni rutinu pripreme sastavljanja priključka kada se koristi install-blob" + msgid "Runtime" msgstr "Vrijeme trajanja" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Spremi stanje uređaja u JSON datoteku između izvršavanja" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Zakaži instalaciju pri sljedećem pokretanju kada je moguće" @@ -581,6 +936,10 @@ msgid "Set release version on firmware file" msgstr "Postavi inačicu izdanja u datoteku firmvera" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Postavi oznaku otklanjanja grešaka tijekom nadopune" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Postavi veličinu frimvera za metu" @@ -593,6 +952,13 @@ msgid "Sets metadata on a firmware file" msgstr "Postavlja metapodatke u datoteku frimvera" +msgid "Sets the list of approved firmware" +msgstr "Postavlja popis odobrenih frimvera" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Postavlja popis odobrenih frimvera" + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Podijeli povijest frimvera sa razvijateljima" @@ -601,15 +967,23 @@ msgid "Show client and daemon versions" msgstr "Prikaži inačicu klijenta i pozadinskog programa" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Prikaži dodatne informacije pozadinskog programa za određenu domenu" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Prikaži informacije otklanjanja greške za sve datoteke" +msgid "Show debugging information for all domains" +msgstr "Prikaži informacije otklanjanja greške za sve domene" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Prikaži mogućnosti otklanjanja greške" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Prikaži uređaje koji se ne mogu nadopuniti" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Prikaži dodatne informacije otklanjanja grešaka" @@ -621,6 +995,38 @@ msgid "Show plugin verbose information" msgstr "Prikaži dodatne informacije priključka" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Prikaži zapis otklanjanja grešaka posljednjeg pokušaja nadopune" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Prikaži informacije stanja nadopune frimvera" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Odmah isključi?" + +msgid "Sign data using the client certificate" +msgstr "Potpiši podatke koristeći vjerodajnicu klijenta" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Potpiši podatke koristeći vjerodajnicu klijenta" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Potpiši poslane podatke s vjerodajnicom klijenta" + +msgid "Signature" +msgstr "Potpis" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Odredi ID-ove Proizvođača/Proizvoda DFU uređaja" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Odredi broj bajtova po USB prijenosu" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Stanje" @@ -638,11 +1044,25 @@ 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: 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 (Frimver Usluga Linux Proizvođača) je besplatna usluga koja djeluje kao neovisna pravna osoba i nema veze sa $OS_RELEASE:NAME$. Vaš distributer možda nije provjerio nadopune frimvera za kompatibilnost s vašim sustavom ili priključenim uređajima. Svi frimveri su pružani od strane izvornih proizvođača opreme." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Ne postoji odobreni frimver." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Ovaj program možda radi ispravno samo ako je pokrenut kao korijenski korisnik" + +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 "Ova udaljena lokacija sadrži frimver koji nije zabranjen, ali se još uvijek testira od strane proizvođača hardvera. Provjerite da imate način na ručno vraćanje starije inačice frimvera ako nadopuna frimvera ne uspije." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Ovaj alat može koristiti samo korijenski korisnik" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -656,6 +1076,10 @@ msgid "Type" msgstr "Vrsta" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI firmver pomagalo" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -671,6 +1095,15 @@ msgid "Unlocks the device for firmware access" msgstr "Otključava uređaj za pristup frimvera" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Ukloni oznaku otklanjanja grešaka tijekom nadopune" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Nepodržana inačica pozadinskog programa %s, inačica klijenta je %s" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Kontrolni zbroj nadopune" @@ -679,6 +1112,11 @@ msgid "Update Description" msgstr "Opis nadopune" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Trajanje nadopune" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Lokacija nadopune" @@ -699,8 +1137,9 @@ msgid "Update Version" msgstr "Inačica nadopune" -msgid "Update device firmware on Linux" -msgstr "Nadopunite frimvere uređaja na Linuxu" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Nadopuni sve uređaje koji se podudaraju s lokalnim metapodacima" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -718,6 +1157,10 @@ msgstr "Nadopuni pohranjene metapodatke s trenutnim sadržajem ROM-a" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Nadopuni pohranjene metapodatke s trenutnim sadržajem" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Nadopuni sav frimver na najnovije dostupne inačice" @@ -728,6 +1171,11 @@ msgid "Updating %s from %s to %s... " msgstr "Nadopuna %s s inačice %s na inačicu %s... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Nadopunjujem %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Pošalji poruku:" @@ -736,6 +1184,10 @@ msgid "Upload report now?" msgstr "Pošalji izvještaj odmah?" +#. TRANSLATORS: explain why we want to upload +msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +msgstr "Slanje izvještaja frimvera pomaže proizvođačima hardvera brzo otkrivanje nedostatka i brzu nadopunu na stvarnim uređajima." + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Korisničko ime" @@ -757,6 +1209,10 @@ msgstr "Nadgledaj odspajanje DFU uređaja" #. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Nadgledaj promjene hardvera" + +#. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "Zapiši frimver iz datoteke u uređaj" @@ -768,5 +1224,6 @@ msgid "Writing…" msgstr "Zapisivanje..." -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 "Vaš distributer možda nije provjerio nadopune frimvera za kompatibilnost s vašim sustavom ili priključenim uređajima." diff -Nru fwupd-1.0.9/po/hu.po fwupd-1.2.10/po/hu.po --- fwupd-1.0.9/po/hu.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/hu.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,17 +3,14 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Balázs Meskó , 2017-2018 -# Balázs Úr , 2015-2018 +# Balázs Meskó , 2017-2019 +# Balázs Úr, 2015-2018 # Gabor Kelemen , 2016 # kelemeng , 2016 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: Hungarian (http://www.transifex.com/freedesktop/fwupd/language/hu/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,11 +18,104 @@ "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: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s vezérlőfrissítés" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s eszközfrissítés" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s beágyazottvezérlő-frissítés" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME frissítés" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s rendszerfrissítés" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s frissítés" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s firmware frissítésekkel rendelkezik:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +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: command description +msgid "Activate devices" +msgstr "Eszközök aktiválása" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Függőben lévő eszközök aktiválása" + +msgid "Activate the new firmware on the device" +msgstr "Az új firmware aktiválása az eszközön" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Firmware frissítés aktiválása" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Firmware frissítés aktiválása ennél:" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Hozzáadva" @@ -34,6 +124,10 @@ 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" @@ -47,10 +141,14 @@ 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." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Egy frissítés a rendszer újraindítását igényel a befejezéshez." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Igen az összes kérdésre" @@ -59,10 +157,25 @@ 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: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Jóváhagyott firmware:" +msgstr[1] "Jóváhagyott firmwarek:" + #. 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" @@ -85,6 +198,18 @@ msgstr "Hitelesítés szükséges a firmware frissítéshez beállított távoli tároló módosításához." #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Hitelesítés szükséges a jóváhagyott firmwarek listájának beállításához" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Hitelesítés szükséges az adatok ügyféltanúsítvánnyal történő aláírásához" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Hitelesítés szükséges az új firmware verzióra váltáshoz" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Hitelesítés szükséges az eszköz feloldásához" @@ -185,6 +310,14 @@ 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: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Eszközazonosító" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Eszköz hozzáadva:" @@ -205,6 +338,17 @@ 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" @@ -217,6 +361,11 @@ 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" + +#. success msgid "Done!" msgstr "Kész!" @@ -231,6 +380,11 @@ 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…" @@ -247,10 +401,36 @@ 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" @@ -271,6 +451,22 @@ msgid "Exit after the engine has loaded" msgstr "Kilépés a motor betöltődése után" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "A démonhoz kapcsolódás sikertelen" + +#. 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: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "A függőben lévő eszközök lekérése sikertelen" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "A firmware frissítés telepítése sikertelen" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "A trükkök betöltése sikertelen" @@ -279,6 +475,14 @@ msgid "Failed to parse arguments" msgstr "Nem sikerült feldolgozni az argumentumokat" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Újraindítás sikertelen" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Az indítóképernyő módjának beállítása sikertelen" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Fájl lekérése" @@ -303,6 +507,10 @@ msgid "Filename Signature" msgstr "Fájlnév aláírása" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Firmware ügynök" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "Firmware kiindulópont URI" @@ -326,6 +534,19 @@ 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." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Jelzők" + +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" @@ -334,10 +555,22 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Az összes eszköz lekérdezése a rendszer topológiájának megfelelően" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Összes eszköz és a lehetséges kiadások lekérése" + +#. 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" @@ -349,6 +582,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Lekéri a kiírt firmware kriptográfiai hash-ét" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Lekéri a jóváhagyott firmwarek listáját." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "A frissítések listáját kéri le a csatlakoztatott hardverhez" @@ -370,16 +607,16 @@ 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" msgid "Install old version of system firmware" msgstr "A rendszer firmware régi verziójának telepítése" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Az előkészített frissítések telepítés most" - msgid "Install signed device firmware" msgstr "Aláírt eszköz firmware telepítése" @@ -392,21 +629,48 @@ msgid "Install unsigned system firmware" msgstr "Nem aláírt rendszer firmware telepítése" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Firmware telepítése…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot 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" @@ -419,6 +683,10 @@ 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" @@ -439,11 +707,26 @@ 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." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nem lett frissítés alkalmazva" + msgid "OK" msgstr "OK" @@ -451,6 +734,10 @@ 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ó" @@ -458,6 +745,10 @@ msgid "Payload" msgstr "Tartalom" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Százalék kész" + msgid "Permission denied" msgstr "Hozzáférés megtagadva" @@ -466,6 +757,12 @@ 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" @@ -477,6 +774,10 @@ 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" @@ -494,6 +795,10 @@ msgid "Reading…" msgstr "Olvasás…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Újraindítás…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Metaadatok frissítése a távoli kiszolgálóról" @@ -547,12 +852,16 @@ #. 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" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Eszköz állapotának mentése JSON fájlba a végrehajtások között" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Telepítés ütemezése a következő újraindításkor, ha lehetséges" @@ -584,6 +893,10 @@ 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" @@ -596,6 +909,13 @@ msgid "Sets metadata on a firmware file" msgstr "Metaadatok beállítása egy firmware fájlon" +msgid "Sets the list of approved firmware" +msgstr "Beállítja a jóváhagyott firmwarek listáját" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Beállítja a jóváhagyott firmwarek listáját." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Firmware frissítése előzmények megosztása a fejlesztőkkel" @@ -604,15 +924,15 @@ msgid "Show client and daemon versions" msgstr "Ügyfél és démon verziók 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" - #. TRANSLATORS: for the --verbose arg 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" @@ -624,6 +944,38 @@ 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" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Leállítja most?" + +msgid "Sign data using the client certificate" +msgstr "Adatok aláírása az ügyféltanúsítvánnyal" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Adatok aláírása az ügyféltanúsítvánnyal" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Feltöltött adatok aláírása az ügyféltanúsítvánnyal" + +msgid "Signature" +msgstr "Aláírás" + +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" @@ -641,11 +993,25 @@ 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. " - -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: 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." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Nincs jóváhagyott firmware." + +#. 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: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Ezt az eszközt csak a root felhasználó használhatja" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -659,6 +1025,10 @@ 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" @@ -674,6 +1044,10 @@ 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" @@ -682,6 +1056,11 @@ 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" @@ -702,8 +1081,9 @@ msgid "Update Version" msgstr "Frissítés verziója" -msgid "Update device firmware on Linux" -msgstr "Eszköz firmware frissítése Linuxon" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Az összes olyan eszköz frissítése, amely illeszkedik a helyi metaadatokra" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -721,6 +1101,10 @@ msgstr "A tárolt metaadatok frissítése a jelenlegi ROM tartalmával" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "A tárolt metaadatok frissítése a jelenlegi tartalommal" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Minden firmware-t az elérhető legfrissebb verziókra frissít" @@ -731,6 +1115,11 @@ 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:" @@ -739,6 +1128,10 @@ 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" @@ -760,6 +1153,10 @@ 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" @@ -771,5 +1168,6 @@ 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 -Nru fwupd-1.0.9/po/id.po fwupd-1.2.10/po/id.po --- fwupd-1.0.9/po/id.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/id.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: Indonesian (http://www.transifex.com/freedesktop/fwupd/language/id/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -44,7 +41,7 @@ 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." @@ -210,6 +207,7 @@ msgid "Do not check for unreported history" msgstr "Jangan periksa untuk riwayat yang tak dilaporkan" +#. success msgid "Done!" msgstr "Selesai!" @@ -368,10 +366,6 @@ msgid "Install old version of system firmware" msgstr "Pasang versi lama dari firmware sistem" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Pasang pemutakhiran yang disiapkan sekarang" - msgid "Install signed device firmware" msgstr "Pasang firmware perangkat yang ditandatangani" @@ -591,10 +585,6 @@ msgid "Show client and daemon versions" msgstr "Tampilkan versi daemon dan klien" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Tampilkan informasi pengawakutuan bagi semua berkas" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Tampilkan opsi pengawakutuan" @@ -628,12 +618,6 @@ 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" @@ -689,9 +673,6 @@ 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:" @@ -757,6 +738,3 @@ #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Menulis..." - -msgid "fwupd" -msgstr "fwupd" diff -Nru fwupd-1.0.9/po/it.po fwupd-1.2.10/po/it.po --- fwupd-1.0.9/po/it.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/it.po 2019-07-15 18:25:54.000000000 +0000 @@ -4,14 +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" -"POT-Creation-Date: 2018-06-06 14:54+0100\n" -"PO-Revision-Date: 2018-06-06 16:23+0000\n" -"Last-Translator: Milo Casagrande \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" @@ -19,11 +16,116 @@ "Language: it\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] "Manca %.0f minuto" +msgstr[1] "Mancano %.0f minuti" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Aggiornamento Consumer ME di %s" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Aggiornamento unità di controllo %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "Aggiornamento Coporate ME di %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Aggiornamento dispositivo %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Aggiornamento unità di controllo integrata di %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Aggiornamento ME di %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Aggiornamento sistema %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Aggiornamento di %s" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s ha degli aggiornamenti del firmware:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +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: command description +msgid "Activate devices" +msgstr "Attiva dispositivi" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Attiva i dispositivi in attesa" + +msgid "Activate the new firmware on the device" +msgstr "Attiva il nuovo firmware sul dispositivo" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Attivazione aggiornamento firmware" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Attivazione aggiornamento firmware per" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Aggiunto" @@ -49,10 +151,14 @@ 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" @@ -61,6 +167,17 @@ msgid "Apply a binary patch" msgstr "Applica una patch binaria" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Applica aggiornamenti firmware" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Firmware approvato:" +msgstr[1] "Firmware approvati:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Collega dispositivo DFU al runtime" @@ -91,6 +208,22 @@ msgstr "È richiesto autenticarsi per modificare un remoto configurato utilizzato per aggiornamenti firmware" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "È richiesto autenticarsi per modificare la configurazione del demone" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "È richiesto autenticarsi per impostare l'elenco dei firmware approvati" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "È richiesto autenticarsi per firmare i dati utilizzando il certificato del client" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "È richiesto autenticarsi per passare alla nuova versione del firmware " + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "È richiesto autenticarsi per sbloccare un dispositivo" @@ -195,6 +328,10 @@ msgid "Detach to bootloader mode" msgstr "Scollega in modalità bootloader" +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "ID dispositivo" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Dispositivo aggiunto:" @@ -215,11 +352,18 @@ msgid "Devices that were not updated correctly:" msgstr "Dispositivi non aggiornati correttamente:" +msgid "Disabled fwupdate debugging" +msgstr "Debug fwupdate disabilitato" + #. TRANSLATORS: command description msgid "Disables a given remote" msgstr "Disabilita un remoto dato" #. TRANSLATORS: command line option +msgid "Display version" +msgstr "Visualizza la versione" + +#. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Non controlla i metadati vecchi" @@ -235,6 +379,7 @@ msgid "Do not write to the history database" msgstr "Non scrive la cronologia" +#. success msgid "Done!" msgstr "Fatto." @@ -249,6 +394,11 @@ 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…" @@ -265,6 +415,14 @@ msgid "Dump information about a binary patch to the screen" msgstr "Stampa le informazioni riguardo a una patch binaria su schermo" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "ESP specificata non era valida" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Abilita il supporto all'aggiornamento firmware sui sistemi compatibili" + #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" msgstr "Abilitare questo remoto?" @@ -273,10 +431,16 @@ msgid "Enabled" msgstr "Abilitato" +msgid "Enabled fwupdate debugging" +msgstr "Debug fwupdate abilitato" + #. TRANSLATORS: command description msgid "Enables a given remote" msgstr "Abilita un remoto dato" +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 "Abilitare questa funzionalità a proprio rischio: in caso di problemi con gli aggiornamenti sarà necessario contattare l'OEM. Solamente i problemi legati al processo di aggiornamento possono essere inviati a $OS_RELEASE:BUG_REPORT_URL$." + #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." msgstr "Abilitare questo remoto a proprio rischio." @@ -301,10 +465,22 @@ msgid "Exit after the engine has loaded" msgstr "Esce dopo che il motore è stato caricato" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Connessione al demone non riuscita" + #. TRANSLATORS: the server is rate-limiting downloads msgid "Failed to download due to server limit" msgstr "Scaricamento non riuscito a causa di un limite sul server" +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Recupero dei dispositivi in attesa non riuscito" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Installazione aggiornamento firmware non riuscita" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Caricamento stranezze non riuscito" @@ -313,6 +489,14 @@ msgid "Failed to parse arguments" msgstr "Analisi degli argomenti non riuscita" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Riavvio non riuscito" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Impostazione della modalità splash non riuscita" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Recupero file" @@ -337,6 +521,10 @@ msgid "Filename Signature" msgstr "Firma nome file" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Strumento firmware" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "URI di base del firmware" @@ -360,6 +548,19 @@ msgstr[0] "I metadati del firmware non sono stati controllati per %u giorno e potrebbero non essere aggiornati." msgstr[1] "I metadati del firmware non sono stati controllati per %u giorni e potrebbero non essere aggiornati." +msgid "Firmware updates are not supported on this machine." +msgstr "Gli aggiornamenti firmware non sono supportati su questo dispositivo." + +msgid "Firmware updates are supported on this machine." +msgstr "Gli aggiornamenti firmware sono supportati su questo dispositivo." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Flag" + +msgid "Force the action ignoring all warnings" +msgstr "Forza l'azione ignorando gli avvisi" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Trovato" @@ -372,6 +573,10 @@ msgstr "Recupera tutti i dispositivi in base alla topologia di sistema" #. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Ottiene tutti i dispositivi e i possibili rilasci" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Ottiene tutti i dispositivi che supportano gli aggiornamenti del firmware" @@ -391,6 +596,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Ottiene l'hash crittografico del firmware scartato" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Recupera l'elenco dei firmware approvati." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Ottiene l'elenco degli aggiornamenti per l'hardware connesso" @@ -422,10 +631,6 @@ msgid "Install old version of system firmware" msgstr "Installa una vecchia versione del firmware di sistema" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Installa ora gli aggiornamenti preparati" - msgid "Install signed device firmware" msgstr "Installa firmware firmato del dispositivo" @@ -438,13 +643,26 @@ msgid "Install unsigned system firmware" msgstr "Installa firmware non firmato di sistema" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Installazione firmware…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Installazione aggiornamento firmware…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installazione su %s…" + msgid "Keyring" msgstr "Portachiavi" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Manca meno di un minuto" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (firmware stabile)" @@ -455,6 +673,10 @@ msgid "List currently attached DFU capable devices" msgstr "Elenca gli attuali dispositivi collegati con supporto DFU" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Elenca gli aggiornamenti firmware supportati" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Caricamento…" @@ -479,9 +701,18 @@ msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadati possono essere scaricati da Linux Vendor Firmware Service." +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Versioni di demone e client non corrispondenti, usare %s" + msgid "Mode" msgstr "Modalità" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Modifica il valore della configurazione del demone" + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Modifica un remoto" @@ -489,6 +720,9 @@ msgid "Modify a configured remote" msgstr "Modifica un remoto configurato" +msgid "Modify daemon configuration" +msgstr "Modifica la configurazione del demone" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Controlla il demone per gli eventi" @@ -499,6 +733,9 @@ msgid "Name" msgstr "Nome" +msgid "No action specified!" +msgstr "Nessuna azione specificata." + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" @@ -512,6 +749,10 @@ msgid "No remotes are currently enabled so no metadata is available." msgstr "Non è abilitato alcun remoto e non sono disponibili metadati." +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Non è stato applicato alcun aggiornamento" + msgid "OK" msgstr "Fatto" @@ -519,6 +760,14 @@ msgid "Override plugin warning" msgstr "Scavalca l'avviso sul plugin" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Sovrascrive il percorso ESP predefinito" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Scavalca gli avvisi e forza l'azione" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Password" @@ -526,6 +775,10 @@ msgid "Payload" msgstr "Carico" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentuale completamento" + msgid "Permission denied" msgstr "Permesso negato" @@ -534,6 +787,12 @@ 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à" @@ -545,6 +804,10 @@ msgid "Protocol" msgstr "Protocollo" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Interroga il supporto per gli aggiornamenti firmware" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -562,6 +825,10 @@ msgid "Reading…" msgstr "Lettura…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Riavvio…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Ricarica i metadati dal server remoto" @@ -609,6 +876,10 @@ msgid "Restart now?" msgstr "Riavviare ora?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Riavviare il demone per applicare le modifiche?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Riavvio del dispositivo…" @@ -617,10 +888,22 @@ msgid "Return all the hardware IDs for the machine" msgstr "Fornisce tutti gli ID hardware per un dispositivo" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Esegue la procedura di pulizia del plugin quando si utilizza install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Esegue la procedura di preparazione del plugin quando si utilizza install-blob" + msgid "Runtime" msgstr "Runtime" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Salva lo stato del dispositivo tra le esecuzioni su un file JSON" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Pianifica l'installazione al prossimo riavvio quando è possibile" @@ -652,6 +935,10 @@ msgid "Set release version on firmware file" msgstr "Imposta la versione di rilascio sul file del firmware" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Imposta il flag di debug durante l'aggiornamento" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Imposta la dimensione del firmware per la destinazione" @@ -664,6 +951,13 @@ msgid "Sets metadata on a firmware file" msgstr "Imposta i metadati su un file di firmware" +msgid "Sets the list of approved firmware" +msgstr "Imposta l'elenco dei firmware approvati" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Imposta l'elenco dei firmware approvati." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Condivide la cronologia del firmware con gli sviluppatori" @@ -672,9 +966,13 @@ 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 for a particular domain" +msgstr "Mostra informazioni prolisse del demone per un dominio" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Mostra le informazioni di debug per tutti i file" +msgid "Show debugging information for all domains" +msgstr "Mostra informazioni di debug per tutti i domini" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" @@ -696,6 +994,38 @@ msgid "Show plugin verbose information" msgstr "Mostra informazioni dettagliate del plugin" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Mostra debug dell'ultimo tentativo di aggiornamento" + +#. TRANSLATORS: command line option +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 "Sign data using the client certificate" +msgstr "Firma i dati col certificato del client" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Firma i dati col certificato del client" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Firma i dati caricati col certificato del client" + +msgid "Signature" +msgstr "Firma" + +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" @@ -713,15 +1043,25 @@ msgid "Target" msgstr "Obiettivo" -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: 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 è 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." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Non ci sono firmware approvati." #. 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." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Questo strumento può essere usato solamente dall'utente root" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -735,6 +1075,10 @@ msgid "Type" msgstr "Tipo" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Strumento firmware UEFI" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -750,6 +1094,15 @@ msgid "Unlocks the device for firmware access" msgstr "Sblocca il dispositivo per accedere al firmware" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Ripristina il flag di debug durante l'aggiornamento" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Demone versione %s non supportato, la versione del client è %s" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Codice di controllo aggiornamento" @@ -758,6 +1111,11 @@ 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" @@ -778,8 +1136,9 @@ 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:" @@ -797,6 +1156,10 @@ msgstr "Aggiorna i metadati salvati con gli attuali contenuti della ROM" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Aggiorna i metadati salvati con il contenuto attuale" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Aggiorna tutti i firmware all'ultima versione disponibile" @@ -807,6 +1170,11 @@ 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:" @@ -858,6 +1226,3 @@ #. 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 -Nru fwupd-1.0.9/po/kk.po fwupd-1.2.10/po/kk.po --- fwupd-1.0.9/po/kk.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/kk.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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 -Nru fwupd-1.0.9/po/ko.po fwupd-1.2.10/po/ko.po --- fwupd-1.0.9/po/ko.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/ko.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,15 +3,12 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Seong-ho Cho , 2017 -# Shinjo Park , 2018 +# Seong-ho Cho , 2017,2019 +# Shinjo Park , 2018-2019 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: Korean (http://www.transifex.com/freedesktop/fwupd/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,40 +16,170 @@ "Language: ko\n" "Plural-Forms: nplurals=1; plural=0;\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f분 남음" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s 소비자용 ME 업데이트" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s 컨트롤러 업데이트" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s 기업용 ME 업데이트" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s 장치 업데이트" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s 임베디드 컨트롤러 업데이트" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME 업데이트" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s 시스템 업데이트" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s 업데이트" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s의 최신 펌웨어가 있습니다:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u일" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u시간" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u분" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u초" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "장치 활성화" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "대기 중인 장치 활성화" + +msgid "Activate the new firmware on the device" +msgstr "장치에 새 펌웨어 활성화" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "펌웨어 업데이트 활성화 중" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "다음 장치의 펌웨어 업데이트 활성화 중:" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "추가함" #. TRANSLATORS: the age of the metadata msgid "Age" -msgstr "경과기간" +msgstr "경과 기간" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "동의하며 원격 설정을 활성화하시겠습니까?" #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" -msgstr "%s(으)로 별칭 부여" +msgstr "%s의 별칭" #. TRANSLATORS: command line option msgid "Allow downgrading firmware versions" -msgstr "펌웨어를 이전 버전으로 되돌릴 수 있게 합니다" +msgstr "펌웨어 다운그레이드를 허용합니다" #. TRANSLATORS: command line option msgid "Allow re-installing existing firmware versions" msgstr "기존 펌웨어 버전 재설치를 허용합니다" +#. 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 "모든 질문에 예로 답하기" + #. TRANSLATORS: command description msgid "Apply a binary patch" msgstr "바이너리 패치 적용" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "펌웨어 업데이트 적용" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "허용 펌웨어:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "DFU 기능 장치를 런타임에 연결" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "펌웨어 모드에 연결" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -64,36 +191,56 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "이동식 장치의 펌웨어를 이전 버전으로 되돌리려면 인증이 필요합니다" +msgstr "이동식 장치의 펌웨어를 이전 버전으로 되돌리려면 인증해야 합니다" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" -msgstr "이 머신에 이전 버전의 펌웨어를 설치하려면 인증이 필요합니다" +msgstr "이 머신에 이전 버전의 펌웨어를 설치하려면 인증해야 합니다" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" -msgstr "펌웨어 업데이트에 사용할 원격 설정을 수정하려면 인증이 필요합니다" +msgstr "펌웨어 업데이트에 사용할 원격 설정을 수정하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "데몬 설정을 수정하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "허용된 펌웨어 목록을 설정하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "클라이언트 인증서로 데이터를 서명하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "새 펌웨어 버전으로 전환하려면 인증해야 합니다" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" -msgstr "장치 잠금을 해제하려면 인증이 필요합니다" +msgstr "장치 잠금을 해제하려면 인증해야 합니다" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on a removable device" -msgstr "이동식 장치의 펌웨어를 업데이트하려면 인증이 필요합니다" +msgstr "이동식 장치의 펌웨어를 업데이트하려면 인증해야 합니다" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" -msgstr "이 머신의 펌웨어를 업데이트하려면 인증이 필요합니다" +msgstr "이 머신의 펌웨어를 업데이트하려면 인증해야 합니다" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the stored checksums for the device" -msgstr "장치 보관 검사합을 업데이트하려면 인증이 필요합니다 " +msgstr "저장된 체크섬을 업데이트하려면 인증해야 합니다" #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "샌드박스에서 펌웨어를 빌드합니다" +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "취소" + #. TRANSLATORS: this is when a device ctrl+c's a watch msgid "Cancelled" msgstr "취소함" @@ -106,7 +253,7 @@ #. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" -msgstr "검사합" +msgstr "체크섬" #. TRANSLATORS: chip ID, e.g. "0x58200204" msgid "Chip ID" @@ -142,7 +289,10 @@ #. TRANSLATORS: command description msgid "Create a binary patch using two files" -msgstr "파일 두 개를 활용하여 바이너리 패치 만들기" +msgstr "파일 두 개를 사용하여 바이너리 패치 만들기" + +msgid "DFU" +msgstr "DFU" #. TRANSLATORS: DFU stands for device firmware update msgid "DFU Utility" @@ -168,18 +318,62 @@ msgid "Detach currently attached DFU capable device" msgstr "현재 연결한 DFU 기능 장치 분리" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "부트로더 모드로 전환" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "장치 ID" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" -msgstr "장치 추가함:" +msgstr "장치 추가됨:" #. TRANSLATORS: this is when a device has been updated msgid "Device changed:" -msgstr "장치 상태 바꿈:" +msgstr "장치 상태 바뀜:" #. TRANSLATORS: this is when a device is hotplugged msgid "Device removed:" -msgstr "장치 제거함:" +msgstr "장치 제거됨:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "성공적으로 업데이트된 장치:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "올바르게 업데이트되지 않은 장치:" + +msgid "Disabled fwupdate debugging" +msgstr "fwupdate 디버깅 비활성화" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "지정한 원격 정보를 비활성화합니다" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "버전 표시" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "오래된 메타데이터 검사하지 않기" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "업데이트 후 다시 시작 검사하지 않기" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "보고되지 않은 과거 기록 검사하지 않기" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "과거 기록 데이터베이스에 기록하지 않기" +#. success msgid "Done!" msgstr "완료!" @@ -194,30 +388,65 @@ msgid "Downgrading %s from %s to %s... " msgstr "%2$s에서 %3$s(으)로 %1$s 다운그레이드 중... " +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "%s 다운그레이드 중..." + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" -msgstr "내려받는 중…" +msgstr "다운로드 중…" #. TRANSLATORS: command description msgid "Dump SMBIOS data from a file" -msgstr "파일의 SMBIOS 데이터 덤프를 출력합니다" +msgstr "파일에 저장된 SMBIOS 데이터 덤프를 출력합니다" #. TRANSLATORS: command description msgid "Dump details about a firmware file" -msgstr "펌웨어 파일 세부 정보 덤프" +msgstr "펌웨어 파일 세부 정보 출력" #. TRANSLATORS: command description msgid "Dump information about a binary patch to the screen" -msgstr "바이너리 패치 정보 덤프 스크린에 출력" +msgstr "바이너리 패치 정보를 화면으로 출력" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "지정한 ESP가 올바르지 않습니다" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "지원하는 시스템의 펌웨어 업데이트 지원 활성화" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "이 원격 설정을 활성화하시겠습니까?" #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "활성화 여부" +msgid "Enabled fwupdate debugging" +msgstr "fwupdate 디버깅 활성화" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "지정한 원격 정보를 활성화합니다" + +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "이 기능의 사용은 본인의 책임이며, 업데이트로 인해서 생긴 문제는 원 장치 제조사에 직접 보고해야 합니다. 업데이트 진행 과정 자체의 문제는 $OS_RELEASE:BUG_REPORT_URL$(으)로 보고해 주십시오." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "이 원격 장치를 설정하는 것은 본인의 책임입니다." + #. TRANSLATORS: command description msgid "Encrypt firmware data" msgstr "펌웨어 데이터 암호화" +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "모든 펌웨어 업데이트 기록 지우기" + #. TRANSLATORS: erasing contents of the flash chips msgid "Erasing…" msgstr "지우는 중…" @@ -230,10 +459,38 @@ msgid "Exit after the engine has loaded" msgstr "엔진을 불러온 후 나가기" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "데몬에 연결할 수 없음" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "서버 제한으로 파일을 다운로드할 수 없음" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "대기 중인 장치를 가져올 수 없음" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "펌웨어 업데이트를 설치할 수 없음" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "특이 사항을 불러올 수 없음" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "인자 해석에 실패했습니다" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "다시 시작할 수 없음" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "스플래시 모드를 설정할 수 없음" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "파일 가져오는 중" @@ -256,7 +513,11 @@ #. TRANSLATORS: filename of the local file msgid "Filename Signature" -msgstr "파일이름 서명" +msgstr "파일 이름 서명" + +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "펌웨어 에이전트" #. TRANSLATORS: remote URI msgid "Firmware Base URI" @@ -274,6 +535,25 @@ msgid "Firmware Utility" msgstr "펌웨어 유틸리티" +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "펌웨어 메타데이터가 %u일 동안 업데이트되지 않았으므로 최신 정보가 누락되었을 수도 있습니다." + +msgid "Firmware updates are not supported on this machine." +msgstr "이 머신에서 펌웨어 업데이트를 지원하지 않습니다." + +msgid "Firmware updates are supported on this machine." +msgstr "이 머신에서 펌웨어 업데이트를 지원합니다." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "플래그" + +msgid "Force the action ignoring all warnings" +msgstr "모든 경고를 무시하고 작업 강제 진행" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "감지 장치" @@ -282,10 +562,22 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "시스템에 연결된 순서대로 모든 장치 목록 가져오기" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "모든 장치와 릴리스 가져오기" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "펌웨어 업데이트를 지원하는 모든 장치 정보를 가져옵니다" #. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "시스템에 등록된 모든 활성 플러그인 가져오기" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "펌웨어 파일 세부 정보를 가져옵니다" @@ -295,7 +587,11 @@ #. TRANSLATORS: command description msgid "Gets the cryptographic hash of the dumped firmware" -msgstr "덤프 펌웨어의 암호화 해시 정보를 가져옵니다" +msgstr "덤프한 펌웨어의 암호화 해시 정보를 가져옵니다" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "허용된 펌웨어 목록을 가져옵니다." #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -303,7 +599,7 @@ #. TRANSLATORS: command description msgid "Gets the releases for a device" -msgstr "장치에 대한 출시 펌웨어를 가져옵니다" +msgstr "장치 펌웨어 릴리스를 가져옵니다" #. TRANSLATORS: command description msgid "Gets the results from the last update" @@ -318,43 +614,70 @@ msgstr "대기 중…" #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "장치에 펌웨어 파일 설치" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "이 하드웨어에 펌웨어 파일을 설치합니다" msgid "Install old version of system firmware" -msgstr "시스템 펌웨어 이전 버전 설치" - -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "현재 준비한 업데이트를 설치합니다" +msgstr "이전 버전 시스템 펌웨어 설치" msgid "Install signed device firmware" -msgstr "서명한 장치 펌웨어 설치" +msgstr "서명된 장치 펌웨어 설치" msgid "Install signed system firmware" -msgstr "서명한 시스템 펌웨어 설치" +msgstr "서명된 시스템 펌웨어 설치" msgid "Install unsigned device firmware" -msgstr "서명하지 않은 장치 펌웨어 설치" +msgstr "서명되지 않은 장치 펌웨어 설치" msgid "Install unsigned system firmware" -msgstr "서명하지 않은 시스템 펌웨어 설치" +msgstr "서명되지 않은 시스템 펌웨어 설치" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "펌웨어 설치 중..." #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "펌웨어 업데이트 설치 중…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "%s에 설치 중..." + msgid "Keyring" msgstr "키 모음" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "1분 미만 남음" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "리눅스 제조사 펌웨어 서비스(안정 버전 펌웨어)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "리눅스 제조사 펌웨어 서비스(테스트 버전 펌웨어)" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "현재 연결한 DFU 기능 장치 목록 보기" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "지원하는 펌웨어 업데이트 목록 표시" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "불러오는 중…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "지정한 플러그인을 수동으로 허용 목록에 추가" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "다중 펌웨어 파일을 하나로 병합" @@ -367,16 +690,32 @@ msgid "Metadata URI Signature" msgstr "메타데이터 URI 서명" +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "리눅스 제조사 펌웨어 서비스에서 메타데이터를 가져올 수 있습니다." + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "데몬과 클라이언트가 일치하지 않음, %s을(를) 대신 사용함" + msgid "Mode" msgstr "모드" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "데몬 설정값을 변경합니다." + #. TRANSLATORS: command description msgid "Modifies a given remote" -msgstr "주어진 원격 정보를 수정합니다" +msgstr "지정한 원격 정보를 수정합니다" msgid "Modify a configured remote" msgstr "원격 설정 수정" +msgid "Modify daemon configuration" +msgstr "데몬 설정 수정" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "데몬 이벤트를 감시합니다" @@ -387,10 +726,25 @@ msgid "Name" msgstr "이름" +msgid "No action specified!" +msgstr "동작을 지정하지 않았습니다!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" -msgstr "펌웨어 업데이트가 가능한 하드웨어가 없습니다" +msgstr "펌웨어를 업데이트할 수 있는 하드웨어가 없음" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "플러그인을 찾을 수 없음" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "원격 자원이 설정되지 않았으므로 메타데이터를 사용할 수 없습니다." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "적용된 업데이트 없음" msgid "OK" msgstr "확인" @@ -399,25 +753,58 @@ msgid "Override plugin warning" msgstr "플러그인 경고 무시" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "기본 ESP 경로 재정의" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "경고를 무시하고 강제로 작업 진행" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "암호" +msgid "Payload" +msgstr "페이로드" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "진행률" + msgid "Permission denied" msgstr "권한 거부" +#. TRANSLATORS: the user isn't reading the question +#, c-format +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 "우선순위" +msgstr "우선 순위" + +msgid "Proceed with upload?" +msgstr "업로드를 진행하시겠습니까?" #. TRANSLATORS: DFU protocol version, e.g. 1.1 msgid "Protocol" msgstr "프로토콜" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "펌웨어 업데이트 지원 조회" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" -msgstr "특이사항" +msgstr "특이 사항" #. TRANSLATORS: command description msgid "Read firmware from device into a file" @@ -425,12 +812,16 @@ #. TRANSLATORS: command description msgid "Read firmware from one partition into a file" -msgstr "파티션의 펌웨어를 읽어 파일에 기록" +msgstr "파티션에서 펌웨어를 읽어 파일에 기록" #. TRANSLATORS: reading from the flash chips msgid "Reading…" msgstr "읽는 중…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "다시 시작하는 중..." + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "원격 서버의 메타데이터를 새로 고칩니다" @@ -462,10 +853,26 @@ msgid "Replace data in an existing firmware file" msgstr "기존 펌웨어 파일의 데이터 교체" +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "보고서 URI" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "인터넷 연결 필요" + #. TRANSLATORS: command description msgid "Reset a DFU device" msgstr "DFU 장치 초기화" +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "지금 다시 시작하시겠습니까?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "데몬을 다시 시작하여 변경 사항을 적용하시겠습니까?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "장치 다시 시작하는 중…" @@ -475,8 +882,23 @@ msgstr "머신의 모든 하드웨어 ID를 반환합니다" #. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "install-blob 사용 시 플러그인 정리 루틴 실행" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "install-blob 사용 시 플러그인 준비 루틴 실행" + +msgid "Runtime" +msgstr "런타임" + +#. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "실행하는 동안의 장치 상태를 JSON 파일로 저장" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" -msgstr "가능하다면 다음 재부팅시 설치 예약" +msgstr "가능하다면 다음에 다시 시작할 때 설치 예약" #. TRANSLATORS: scheduing an update to be done on the next boot msgid "Scheduling…" @@ -504,7 +926,11 @@ #. TRANSLATORS: command description msgid "Set release version on firmware file" -msgstr "펌웨어 파일의 출시 버전 설정" +msgstr "펌웨어 파일의 릴리스 버전 설정" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "업데이트 중 디버깅 플래그 설정" #. TRANSLATORS: command description msgid "Set the firmware size for the target" @@ -512,32 +938,87 @@ #. TRANSLATORS: command description msgid "Set vendor ID on firmware file" -msgstr "펌웨어 파일의 제조자 ID 설정" +msgstr "펌웨어 파일의 제조사 ID 설정" #. TRANSLATORS: command description msgid "Sets metadata on a firmware file" msgstr "펌웨어 파일의 메타데이터 설정" +msgid "Sets the list of approved firmware" +msgstr "허용된 펌웨어 목록 설정" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "허용된 펌웨어 목록을 설정합니다." + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "개발사와 펌웨어 업데이트 기록 공유하기" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "클라이언트와 데몬 버전 표시" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "지정한 도메인의 자세한 데몬 정보 표시" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "모든 파일의 디버깅 정보 표시" +msgid "Show debugging information for all domains" +msgstr "모든 도메인의 디버깅 정보 표시" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "디버깅 옵션을 표시합니다" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "업데이트할 수 없는 장치 표시" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "추가 디버깅 정보 표시" +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "펌웨어 업데이트 기록 보기" + #. TRANSLATORS: this is for plugin development msgid "Show plugin verbose information" msgstr "자세한 플러그인 정보 표시" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "마지막으로 시도한 업데이트의 디버그 정보 표시" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "펌웨어 업데이트 상태 표시" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "지금 종료하시겠습니까?" + +msgid "Sign data using the client certificate" +msgstr "클라이언트 인증서로 데이터 서명" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "클라이언트 인증서로 데이터 서명" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "클라이언트 인증서로 업로드된 데이터 서명" + +msgid "Signature" +msgstr "서명" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "DFU 장치의 제조사/제품 ID 지정" + +msgid "Specify the number of bytes per USB transfer" +msgstr "USB 전송 당 바이트 수 지정" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "상태" @@ -548,11 +1029,32 @@ 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 "이 프로젝트에서는 리눅스에서 자동으로 안전하고 믿을 수 있게 펌웨어를 업데이트할 수 있게 하려고 합니다. 업데이트를 보고 적용할 때 그놈 소프트웨어, 명령행 도구를 쓰거나 D-Bus 인터페이스를 바로 쓸 수 있습니다." +#. TRANSLATORS: section header for the release one line summary +msgid "Summary" +msgstr "요약" + +msgid "Target" +msgstr "대상" + +#. TRANSLATORS: do not translate the variables marked using $ +msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." +msgstr "LVFS는 $OS_RELEASE:NAME$와(과) 별개로 운영되는 독립된 법적 단체에서 운영하는 무료 서비스입니다. 배포판 개발사에서 펌웨어 업데이트와 시스템 및 연결한 장치의 호환성을 검증한다는 보장이 없습니다. 모든 펌웨어는 장치 제조사(OEM)에서 직접 제공합니다." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "허용된 펌웨어가 없습니다." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "이 프로그램은 루트 권한으로만 올바르게 작동할 수도 있습니다" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "이 원격 저장소에서는 하드웨어 제조사에서 검증 단계를 진행 중인 펌웨어를 배포합니다. 펌웨어 업데이트 도중 및 이후 문제가 발생했을 경우를 대비하여 직접 펌웨어를 다운그레이드할 방편을 확보해두시는게 좋습니다." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "이 도구는 루트로만 사용할 수 있습니다" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -566,6 +1068,10 @@ msgid "Type" msgstr "형식" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI 펌웨어 유틸리티" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -579,41 +1085,76 @@ #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" -msgstr "펌웨어 접근시 장치 잠금을 해제합니다" +msgstr "펌웨어에 접근할 수 있도록 장치 잠금을 해제합니다" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "업데이트 중 디버깅 플래그 설정 해제" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "지원하지 않는 데몬 버전 %s, 클라이언트 버전 %s" #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" -msgstr "업데이트 검사합" +msgstr "업데이트 체크섬" #. TRANSLATORS: section header for long firmware desc msgid "Update Description" msgstr "업데이트 설명" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "업데이트 예상 시간" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "업데이트 위치" +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "업데이트 이름" + #. TRANSLATORS: section header for remote ID, e.g. lvfs-testing msgid "Update Remote ID" msgstr "업데이트 원격 ID" +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "업데이트 요약" + #. TRANSLATORS: section header for firmware version msgid "Update Version" msgstr "업데이트 버전" -msgid "Update device firmware on Linux" -msgstr "리눅스에서 장치 펌웨어를 업데이트합니다" +#. 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:" +msgstr "알 수 없는 이유로 업데이트가 실패했습니다. 더 많은 정보를 보려면 다음 URL을 참조가힙시오:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "지금 업데이트하시겠습니까?" msgid "Update the stored device verification information" -msgstr "보관된 장치 검증 정보 업데이트" +msgstr "저장된 장치 검증 정보 업데이트" #. TRANSLATORS: command description msgid "Update the stored metadata with current ROM contents" -msgstr "현재 ROM에 저장한 메타데이터를 업데이트합니다" +msgstr "저장된 메타데이터를 현재 ROM 내용으로 업데이트합니다" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "저장된 메타데이터를 현재 내용으로 업데이트" #. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" -msgstr "모든 펌웨어를 가용 최신 버전으로 업데이트합니다" +msgstr "모든 펌웨어를 사용할 수 있는 최신 버전으로 업데이트합니다" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are @@ -622,6 +1163,23 @@ msgid "Updating %s from %s to %s... " msgstr "%2$s에서 %3$s(으)로 %1$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 "업로드 메시지:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "지금 보고서를 업로드하시겠습니까?" + +#. TRANSLATORS: explain why we want to upload +msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +msgstr "펌웨어 보고서를 업로드하면 하드웨어 제작사에서 실제 장치에 업데이트를 적용했을 때 성공 및 실패 여부를 빠르게 알 수 있습니다." + #. TRANSLATORS: remote filename base msgid "Username" msgstr "사용자 이름" @@ -643,6 +1201,10 @@ msgstr "DFU 핫 플러그 장치 보기" #. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "하드웨어 변경 사항 감시" + +#. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "파일의 펌웨어를 장치에 기록" @@ -654,5 +1216,6 @@ msgid "Writing…" msgstr "쓰는 중…" -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 "배포판 개발사에서 펌웨어 업데이트와 시스템 및 연결된 장치간의 호환성을 검증한다는 보장이 없습니다." diff -Nru fwupd-1.0.9/po/ky.po fwupd-1.2.10/po/ky.po --- fwupd-1.0.9/po/ky.po 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/po/ky.po 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Ilyas Bakirov , 2018 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: ky\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Кошулду" + +#. TRANSLATORS: chip ID, e.g. "0x58200204" +msgid "Chip ID" +msgstr "Чиптин ID'си" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Табылды" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "ID" + +#. show message in progressbar +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing %s" +msgstr "Орнотулууда: %s" + +msgid "Mode" +msgstr "Режими" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Протокол" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Окуулууда..." + +#. TRANSLATORS: device state, i.e. appIDLE +msgid "State" +msgstr "Абалы" + +#. TRANSLATORS: probably not run as root... +#. TRANSLATORS: device has failed to report status +#. TRANSLATORS: device status, e.g. "OK" +msgid "Status" +msgstr "Статус" + +#. TRANSLATORS: currect daemon status is unknown +msgid "Unknown" +msgstr "Белгисиз" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Жазылууда..." + +msgid "fwupd" +msgstr "fwupd" diff -Nru fwupd-1.0.9/po/LINGUAS fwupd-1.2.10/po/LINGUAS --- fwupd-1.0.9/po/LINGUAS 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/LINGUAS 2019-07-15 18:25:54.000000000 +0000 @@ -1,8 +1,11 @@ +af ast ca cs +da de en_GB +eo eu fi fr @@ -15,6 +18,8 @@ it kk ko +ky +lt nl oc pl diff -Nru fwupd-1.0.9/po/lt.po fwupd-1.2.10/po/lt.po --- fwupd-1.0.9/po/lt.po 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/po/lt.po 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,1119 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Moo, 2019 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Lithuanian (http://www.transifex.com/freedesktop/fwupd/language/lt/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: lt\n" +"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s valdiklio atnaujinimas" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s įrenginio atnaujinimas" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s įtaisytojo valdiklio atnaujinimas" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME atnaujinimas" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s sisteminis atnaujinimas" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s atnaujinimas" + +#. TRANSLATORS: first replacement is device name +#, c-format +msgid "%s has firmware updates:" +msgstr "%s turi programinės aparatinės įrangos atnaujinimų:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktyvuoti įrenginius" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktyvuoti laukiančius įrenginius" + +msgid "Activate the new firmware on the device" +msgstr "Aktyvuoti naują programinę aparatinę įrangą įrenginyje" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktyvuojamas programinės aparatinės įrangos atnaujinimas" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktyvuojama programinė aparatinė įranga, skirta" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Pridėtas" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Amžius" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Sutikti ir įjungti šią nuotolinę saugyklą?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alternatyvusis %s pavadinimas" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Leisti sendinti programinės aparatinės įrangos versijas" + +#. TRANSLATORS: command line option +msgid "Allow re-installing existing firmware versions" +msgstr "Leisti iš naujo įdiegti esamas programinės aparatinės įrangos versijas" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Atnaujinimo užbaigimui, reikia paleisti sistemą iš naujo." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Atnaujinimo užbaigimui, reikia išjungti sistemą." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Atsakyti taip į visus klausimus" + +#. TRANSLATORS: command description +msgid "Apply a binary patch" +msgstr "Taikyti dvejetainę pataisą" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Taikyti programinės aparatinės įrangos atnaujinimus" + +#. TRANSLATORS: command description +msgid "Attach DFU capable device back to runtime" +msgstr "Prijungti ĮPAĮA įgalintą įrenginį atgal prie vykdymo aplinkos" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Pridėti į programinės aparatinės įrangos veikseną" + +#. TRANSLATORS: device attributes, i.e. things that +#. * the device can do +msgid "Attributes" +msgstr "Požymiai" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Nustatoma tapatybė…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Norint keičiamajame įrenginyje sendinti programinę aparatinę įrangą, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Norint šiame kompiuteryje sendinti programinę aparatinę įrangą, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Norint modifikuoti programinės aparatinės įrangos atnaujinimams naudojamą sukonfigūruotą saugyklą, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Norint nustatyti patvirtintos programinės aparatinės įrangos sąrašą, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Norint pasirašyti duomenis naudojant kliento liudijimą, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Norint perjungti į naują programinės aparatinės įrangos versiją, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Norint atrakinti įrenginį, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Norint keičiamajame įrenginyje atnaujinti programinę aparatinę įrangą, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Norint šiame kompiuteryje atnaujinti programinę aparatinę įrangą, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Norint atnaujinti įrenginiui saugomas kontrolines sumas, reikalingas tapatybės nustatymas" + +#. TRANSLATORS: command description +msgid "Build firmware using a sandbox" +msgstr "Sukurti programinę aparatinę įrangą, naudojant smėliadėžę" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Atsisakyti" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Atsisakyta" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Pakeistas" + +#. TRANSLATORS: section header for firmware checksum +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrolinė suma" + +#. TRANSLATORS: chip ID, e.g. "0x58200204" +msgid "Chip ID" +msgstr "Lusto ID" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Pasirinkite įrenginį:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Pasirinkite laidą:" + +#. TRANSLATORS: this is the encryption method used when writing +msgid "Cipher" +msgstr "Šifras" + +#. TRANSLATORS: command description +msgid "Clears any updates scheduled to be updated offline" +msgstr "Išvalo visus autonominiam atnaujinimui suplanuotus atnaujinimus" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Išvalo rezultatus iš paskutinio atnaujinimo" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Komanda nerasta" + +#. TRANSLATORS: command description +msgid "Convert firmware to DFU format" +msgstr "Konvertuoti programinę aparatinė įrangą į ĮPAĮA formatą" + +#. TRANSLATORS: command description +msgid "Create a binary patch using two files" +msgstr "Sukurti dvejetainę pataisą, naudojant du failus" + +msgid "DFU" +msgstr "ĮPAĮA" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "ĮPAĮA paslaugų programa" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Derinimo parametrai" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Išskleidžiama…" + +#. TRANSLATORS: command description +msgid "Decrypt firmware data" +msgstr "Iššifruoti programinės aparatinės įrangos duomenis" + +#. TRANSLATORS: section header for firmware description +msgid "Description" +msgstr "Aprašas" + +#. TRANSLATORS: command description +msgid "Detach currently attached DFU capable device" +msgstr "Atjungti šiuo metu prijungtą ĮPAĮA įgalintą įrenginį" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Atskirti į pradinio įkėliklio veikseną" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Įrenginio ID" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Pridėtas įrenginys:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Pakeistas įrenginys:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Pašalintas įrenginys:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Įrenginiai, kurie buvo sėkmingai atnaujinti:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Įrenginiai, kurie nebuvo teisingai atnaujinti:" + +msgid "Disabled fwupdate debugging" +msgstr "Išjungtas fwupdate derinimas" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Išjungia nurodytą nuotolinę saugyklą" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Rodyti versiją" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Netikrinti ar yra senų metaduomenų" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Po atnaujinimo netikrinti ar buvo paleidimas iš naujo" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Netikrinti ar yra istorijos apie kurią nepranešta" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Nerašyti į istorijos duomenų bazę" + +#. success +msgid "Done!" +msgstr "Atlikta!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Sendina programinę aparatinę įrangą įrenginyje" + +#. 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 "Sendinama %s iš %s į %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Sendinamas %s…" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Atsisiunčiama…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Iškloti SMBIOS duomenis iš failo" + +#. TRANSLATORS: command description +msgid "Dump details about a firmware file" +msgstr "Iškloti išsamesnę informaciją apie programinės aparatinės įrangos failą" + +#. TRANSLATORS: command description +msgid "Dump information about a binary patch to the screen" +msgstr "Iškloti į ekraną informaciją apie dvejetainę pataisą" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Nurodytas ESS (angl. ESP) nebuvo teisingas" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Įjungti programinės aparatinės įrangos atnaujinimo palaikymą palaikomose sistemose" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Įjungti šią nuotolinę saugyklą?" + +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Įjungta" + +msgid "Enabled fwupdate debugging" +msgstr "Įjungtas fwupdate derinimas" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Įjungia nurodytą nuotolinę saugyklą" + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Šios nuotolinės saugyklos įjungimas yra jūsų pačių rizika." + +#. TRANSLATORS: command description +msgid "Encrypt firmware data" +msgstr "Šifruoti programinės aparatinės įrangos duomenis" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Ištrinti visą programinės aparatinės įrangos atnaujinimų istoriją" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Ištrinama…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Išeiti po nedidelės delsos" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Išeiti, įkėlus modulį" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Nepavyko prisijungti prie tarnybos" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Nepavyko atsisiųsti dėl serverio apribojimo" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Nepavyko gauti laukiančių įrenginių" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Nepavyko įdiegti programinės aparatinės įrangos atnaujinimo" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Nepavyko įkelti gudrybių" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Nepavyko išanalizuoti argumentų" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Nepavyko paleisti iš naujo" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Nepavyko nustatyti prisistatymo lango veikseną" + +#. TRANSLATORS: downloading unknown file +msgid "Fetching file" +msgstr "Gaunamas failas" + +#. TRANSLATORS: downloading new firmware file +msgid "Fetching firmware" +msgstr "Gaunama programinė aparatinė įranga" + +#. TRANSLATORS: downloading new metadata file +msgid "Fetching metadata" +msgstr "Gaunami metaduomenys" + +#. TRANSLATORS: downloading new signing file +msgid "Fetching signature" +msgstr "Gaunamas parašas" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Failo pavadinimas" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Failo pavadinimo parašas" + +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Programinės aparatinės įrangos agentas" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Pagrindinis programinės aparatinės įrangos URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Programinės aparatinės įrangos atnaujinimo D-Bus tarnyba" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Programinės aparatinės įrangos atnaujinimo tarnyba" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Programinės aparatinės įrangos paslaugų programa" + +msgid "Firmware updates are not supported on this machine." +msgstr "Šiame kompiuteryje programinės aparatinės įrangos atnaujinimai yra neprieinami." + +msgid "Firmware updates are supported on this machine." +msgstr "Šiame kompiuteryje yra prieinami programinės aparatinės įrangos atnaujinimai." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Vėliavėlės" + +msgid "Force the action ignoring all warnings" +msgstr "Priverstinai atlikti veiksmą nepaisant visų įspėjimų" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Rastas" + +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Gauti visus įrenginius pagal sistemos topologiją" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Gauti visus įrenginius ir galimas laidas" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Gauti visus įrenginius, kurie palaiko programinės aparatinės įrangos atnaujinimus" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Gauti visus įjungtus sistemoje registruotus įskiepius" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Gauna išsamesnę informaciją apie programinės aparatinės įrangos failą" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Gauna sukonfigūruotas nuotolines saugyklas" + +#. TRANSLATORS: command description +msgid "Gets the cryptographic hash of the dumped firmware" +msgstr "Gauna išklotos programinės aparatinės įrangos šifravimo maišą" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Gauna patvirtintos programinės aparatinės įrangos sąrašą." + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Gauna atnaujinimų sąrašą prijungtai aparatinei įrangai" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Gauna laidas įrenginiui" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Gauna rezultatus iš paskutinio atnaujinimo" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "ID" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Neveiklus…" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Įdiegti įrenginyje programinės aparatinės įrangos dvejetainį išplėstinį objektą" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Įdiegti šioje aparatinėje įrangoje programinės aparatinės įrangos failą" + +msgid "Install old version of system firmware" +msgstr "Įdiegti senąją sistemos programinės aparatinės įrangos versiją" + +msgid "Install signed device firmware" +msgstr "Įdiegti pasirašytą įrenginio programinę aparatinę įrangą" + +msgid "Install signed system firmware" +msgstr "Įdiegti pasirašytą sistemos programinę aparatinę įrangą" + +msgid "Install unsigned device firmware" +msgstr "Įdiegti nepasirašytą įrenginio programinę aparatinę įrangą" + +msgid "Install unsigned system firmware" +msgstr "Įdiegti nepasirašytą sistemos programinę aparatinę įrangą" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Įdiegiama programinė aparatinė įranga…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Įdiegiamas programinės aparatinės įrangos atnaujinimas…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Įdiegiama ties %s…" + +msgid "Keyring" +msgstr "Raktinė" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Liko mažiau kaip viena minutė" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux tiekėjų programinės aparatinės įrangos paslauga (stabili programinė aparatinė įranga)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux tiekėjų programinės aparatinės įrangos paslauga (testuojama programinė aparatinė įranga)" + +#. TRANSLATORS: command description +msgid "List currently attached DFU capable devices" +msgstr "Išvardyti šiuo metu prijungtus ĮPAĮA įgalintus įrenginius" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Išvardyti prieinamus programinės aparatinės įrangos atnaujinimus" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Įkeliama…" + +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Rankiniu būdu įtraukti tam tikrus įskiepius į baltąjį sąrašą" + +#. TRANSLATORS: command description +msgid "Merge multiple firmware files into one" +msgstr "Sujungti kelis programinės aparatinės įrangos failus į vieną" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metaduomenų URI" + +#. TRANSLATORS: remote URI +msgid "Metadata URI Signature" +msgstr "Metaduomenų URI parašas" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metaduomenys gali būti gauti iš Linux tiekėjų programinės aparatinės įrangos paslaugos." + +msgid "Mode" +msgstr "Veiksena" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifikuoja nurodytą nuotolinę saugyklą" + +msgid "Modify a configured remote" +msgstr "Modifikuoti sukonfigūruotą nuotolinę saugyklą" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Stebėti tarnybą, ar yra įvykių" + +#. TRANSLATORS: interface name, e.g. "Flash" +#. TRANSLATORS: device name, e.g. 'ColorHug2' +#. TRANSLATORS: section header for the release name +msgid "Name" +msgstr "Pavadinimas" + +msgid "No action specified!" +msgstr "Nenurodytas joks veiksmas!" + +#. TRANSLATORS: nothing attached +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Neaptikta jokios aparatinės įrangos su programinės aparatinės įrangos atnaujinimo galimybėmis" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nerasta jokių įskiepių" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Šiuo metu nėra įjungtos jokios nuotolinės saugyklos, taigi, nėra prieinami jokie metaduomenys." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nebuvo pritaikyti jokie atnaujinimai" + +msgid "OK" +msgstr "Gerai" + +#. TRANSLATORS: command line option +msgid "Override plugin warning" +msgstr "Nustelbti įskiepio įspėjimą" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Nustelbti numatytąjį ESS (angl. ESP) kelią" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Slaptažodis" + +msgid "Payload" +msgstr "Naudingoji apkrova" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Užbaigta procentinė dalis" + +msgid "Permission denied" +msgstr "Leidimas atmestas" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Įveskite skaičių nuo 0 iki %u: " + +msgid "Print the version number" +msgstr "Parodyti versijos numerį" + +msgid "Print verbose debug statements" +msgstr "Parodyti išsamius derinimo teiginius" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Pirmenybė" + +msgid "Proceed with upload?" +msgstr "Tęsti išsiuntimą?" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Protokolas" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Užklausti programinės aparatinės įrangos atnaujinimų palaikymo" + +#. TRANSLATORS: device quirks, i.e. things that +#. * it does that we have to work around +msgid "Quirks" +msgstr "Gudrybės" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Skaityti programinę aparatinę įrangą iš įrenginio į failą" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Skaityti programinę aparatinę įrangą iš vieno skaidinio į failą" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Skaitoma…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Paleidžiama iš naujo…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Iš naujo įkelti metaduomenis iš nuotolinio serverio" + +#. TRANSLATORS: these are areas of memory on the chip +msgid "Region" +msgstr "Sritis" + +#. 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 "Iš naujo įdiegiama %s su %s... " + +#. TRANSLATORS: section header for the remote the file is coming from +msgid "Remote" +msgstr "Nuotolinė saugykla" + +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Nuotolinės saugyklos ID" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Pašalintas" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Pakeisti duomenis esamame programinės aparatinės įrangos faile" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Ataskaitų URI" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Reikalauja interneto ryšio" + +#. TRANSLATORS: command description +msgid "Reset a DFU device" +msgstr "Atstatyti ĮPAĮA įrenginį" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Paleisti iš naujo dabar?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Įrenginys paleidžiamas iš naujo…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Grąžinti visus kompiuterio aparatinės įrangos ID" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Paleisti sudėtinę įskiepio išvalymo programą, naudojant install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Paleisti sudėtinę įskiepio paruošimo programą, naudojant install-blob" + +msgid "Runtime" +msgstr "Vykdymo aplinka" + +#. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Įrašyti įrenginio būseną tarp paleidimų į JSON failą" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Kai įmanoma, suplanuoti įdiegimą kitam paleidimui iš naujo" + +#. TRANSLATORS: scheduing an update to be done on the next boot +msgid "Scheduling…" +msgstr "Suplanuojama…" + +#. TRANSLATORS: serial number, e.g. '00012345' +msgid "Serial" +msgstr "Serijos numeris" + +#. TRANSLATORS: command description +msgid "Set alternative name on firmware file" +msgstr "Nustatyti programinės aparatinės įrangos faile alternatyvų pavadinimą" + +#. TRANSLATORS: command description +msgid "Set alternative number on firmware file" +msgstr "Nustatyti programinės aparatinės įrangos faile alternatyvų skaičių" + +#. TRANSLATORS: command description +msgid "Set element address on firmware file" +msgstr "Nustatyti elemento adresą programinės aparatinės įrangos faile" + +#. TRANSLATORS: command description +msgid "Set product ID on firmware file" +msgstr "Nustatyti programinės aparatinės įrangos faile produkto ID" + +#. TRANSLATORS: command description +msgid "Set release version on firmware file" +msgstr "Nustatyti programinės aparatinės įrangos faile laidos versiją" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Atnaujinimo metu nustatyti derinimo vėliavėlę" + +#. TRANSLATORS: command description +msgid "Set the firmware size for the target" +msgstr "Nustatyti programinės aparatinės įrangos dydį, skirtą paskirties objektui" + +#. TRANSLATORS: command description +msgid "Set vendor ID on firmware file" +msgstr "Nustatyti programinės aparatinės įrangos faile tiekėjo ID" + +#. TRANSLATORS: command description +msgid "Sets metadata on a firmware file" +msgstr "Nustato metaduomenis programinės aparatinės įrangos faile" + +msgid "Sets the list of approved firmware" +msgstr "Nustato patvirtintą programinės aparatinės įrangos sąrašą" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Nustato patvirtintos programinės aparatinės įrangos sąrašą." + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Bendrinti programinės aparatinės įrangos istoriją su plėtotojais" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Rodyti kliento ir tarnybos versijas" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Rodyti derinimo parametrus" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Rodyti negalimus atnaujinti įrenginius" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Rodyti papildomą derinimo informaciją" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Rodyti programinės aparatinės įrangos atnaujinimų istoriją" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Rodyti išsamią įskiepio informaciją" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Rodyti derinimo žurnalą iš paskutinio bandyto atnaujinimo" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Rodyti programinės aparatinės įrangos atnaujinimo būseną" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Išjungti dabar?" + +msgid "Sign data using the client certificate" +msgstr "Pasirašyti duomenis, naudojant kliento liudijimą" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Pasirašyti duomenis, naudojant kliento liudijimą" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Pasirašyti išsiunčiamus duomenis naudojant kliento liudijimą" + +msgid "Signature" +msgstr "Parašas" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Nurodyti ĮPAĮA įrenginio tiekėją/produkto ID" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Nurodyti baitų skaičių tenkantį vienam USB persiuntimui" + +#. TRANSLATORS: device state, i.e. appIDLE +msgid "State" +msgstr "Būsena" + +#. TRANSLATORS: probably not run as root... +#. TRANSLATORS: device has failed to report status +#. TRANSLATORS: device status, e.g. "OK" +msgid "Status" +msgstr "Būsena" + +#. TRANSLATORS: section header for the release one line summary +msgid "Summary" +msgstr "Santrauka" + +msgid "Target" +msgstr "Paskirtis" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Nėra jokios patvirtintos programinės aparatinės įrangos." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Ši programa gali tinkamai veikti tik pagrindinio naudotojo teisėmis" + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Šį įrankį gali naudoti tik pagrindinis naudotojas" + +#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" +msgid "Title" +msgstr "Pavadinimas" + +#. TRANSLATORS: transfer size in bytes +msgid "Transfer Size" +msgstr "Persiuntimo dydis" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipas" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI programinės aparatinės įrangos paslaugų programa" + +#. TRANSLATORS: section header for firmware URI +msgid "URI" +msgstr "URI" + +#. TRANSLATORS: currect daemon status is unknown +msgid "Unknown" +msgstr "Nežinoma" + +msgid "Unlock the device to allow access" +msgstr "Atrakinti įrenginį, kad būtų leista prieiga" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Atrakina įrenginį programinės aparatinės įrangos prieigai" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Atnaujinimo metu panaikinti derinimo vėliavėlės nustatymą" + +#. TRANSLATORS: section header for firmware checksum +msgid "Update Checksum" +msgstr "Atnaujinimo kontrolinė suma" + +#. TRANSLATORS: section header for long firmware desc +msgid "Update Description" +msgstr "Atnaujinimo aprašas" + +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Atnaujinimo trukmė" + +#. TRANSLATORS: section header for firmware remote http:// +msgid "Update Location" +msgstr "Atnaujinimo vieta" + +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "Atnaujinimo pavadinimas" + +#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing +msgid "Update Remote ID" +msgstr "Atnaujinimų nuotolinės saugyklos ID" + +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "Atnaujinimo santrauka" + +#. TRANSLATORS: section header for firmware version +msgid "Update Version" +msgstr "Atnaujinimo versija" + +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Atnaujinti visus įrenginius, kurie atitinka vietinius metaduomenis" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Atnaujinimo nesėkmė yra žinoma problema, išsamesnei informacijai apsilankykite šiame URL:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Atnaujinti dabar?" + +msgid "Update the stored device verification information" +msgstr "Atnaujinti saugomo įrenginio patikrinimo informaciją" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current ROM contents" +msgstr "Atnaujinti saugomus metaduomenis esamu ROM turiniu" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Atnaujinti saugomus metaduomenis esamu turiniu" + +#. TRANSLATORS: command description +msgid "Updates all firmware to latest versions available" +msgstr "Atnaujina visą programinę aparatinę įrangą iki naujausios prieinamos versijos" + +#. 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 "Atnaujinama %s iš %s į %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Atnaujinama %s…" + +#. TRANSLATORS: the server sent the user a small message +msgid "Upload message:" +msgstr "Išsiuntimo žinutė:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Išsiųsti ataskaitą dabar?" + +#. 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 "Programinės aparatinės įrangos ataskaitų išsiuntimas padeda aparatinės įrangos tiekėjams greitai atpažinti nesėkmingus bei sėkmingus atnaujinimus tikruose įrenginiuose." + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Naudotojo vardas" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Patikrinama…" + +#. TRANSLATORS: section header for release version number +msgid "Version" +msgstr "Versija" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Laukiama…" + +#. TRANSLATORS: command description +msgid "Watch DFU devices being hotplugged" +msgstr "Stebėti ĮPAĮA įrenginius, kurie atnaujinami nepaleidžiant iš naujo" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Stebėti aparatinės įrangos pakeitimus" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Rašyti programinę aparatinę įrangą iš failo į įrenginį" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Rašyti programinę aparatinę įrangą iš failo į vieną skaidinį" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Rašoma…" + +#. 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 "Gali būti, kad jūsų platintojas, suderinamumui su jūsų sistema ar prijungtais įrenginiais, nėra patvirtinęs jokių programinės aparatinės įrangos atnaujinimų." diff -Nru fwupd-1.0.9/po/make-images fwupd-1.2.10/po/make-images --- fwupd-1.0.9/po/make-images 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/make-images 2019-07-15 18:25:54.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/python3 """ This thing rasterizes text for use later """ # pylint: disable=wrong-import-position,too-many-locals,unused-argument diff -Nru fwupd-1.0.9/po/make-images.sh fwupd-1.2.10/po/make-images.sh --- fwupd-1.0.9/po/make-images.sh 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/make-images.sh 2019-07-15 18:25:54.000000000 +0000 @@ -5,8 +5,12 @@ # # Distributed under terms of the GPLv2 license. # -install -m 0755 -d ${MESON_INSTALL_DESTDIR_PREFIX}/share/locale/ -${MESON_SOURCE_ROOT}/po/make-images "Installing firmware update…" ${MESON_INSTALL_DESTDIR_PREFIX}/share/locale/ ${MESON_SOURCE_ROOT}/po/LINGUAS -for x in ${MESON_INSTALL_DESTDIR_PREFIX}/share/locale/*/LC_IMAGES/*.bmp ; do + +LOCALEDIR="${DESTDIR}$1" +PYTHON3="$2" + +install -m 0755 -d $LOCALEDIR +${PYTHON3} ${MESON_SOURCE_ROOT}/po/make-images "Installing firmware update…" $LOCALEDIR ${MESON_SOURCE_ROOT}/po/LINGUAS +for x in ${LOCALEDIR}/*/LC_IMAGES/*.bmp ; do gzip -fn9 ${x} done diff -Nru fwupd-1.0.9/po/meson.build fwupd-1.2.10/po/meson.build --- fwupd-1.0.9/po/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -5,6 +5,12 @@ ] ) -if get_option('plugin_uefi_labels') -meson.add_install_script('make-images.sh') +if get_option('plugin_uefi') +meson.add_install_script('make-images.sh', localedir, python3.path()) endif + +run_target('fix-translations', + command: [ + join_paths(meson.source_root(), 'contrib/fix_translations.py'), + join_paths(meson.source_root(), 'po') + ]) diff -Nru fwupd-1.0.9/po/nl.po fwupd-1.2.10/po/nl.po --- fwupd-1.0.9/po/nl.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/nl.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: Dutch (http://www.transifex.com/freedesktop/fwupd/language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -125,6 +122,7 @@ msgid "Device removed:" msgstr "Apparaat verwijderd:" +#. success msgid "Done!" msgstr "Afgerond!" @@ -209,10 +207,6 @@ msgid "Install old version of system firmware" msgstr "Oude versie van systeemfirmware installeren" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Voorbereide updates nu installeren" - msgid "Install signed device firmware" msgstr "Ondertekende apparaatfirmware installeren" @@ -350,10 +344,6 @@ msgid "Sets metadata on a firmware file" msgstr "Stelt metadata in op een firmware-bestand" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Foutopsporingsinformatie weergeven voor alle bestanden" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Foutopsporingsopties weergeven" diff -Nru fwupd-1.0.9/po/oc.po fwupd-1.2.10/po/oc.po --- fwupd-1.0.9/po/oc.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/oc.po 2019-07-15 18:25:54.000000000 +0000 @@ -9,9 +9,6 @@ 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: 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" @@ -58,6 +55,7 @@ msgid "Description" msgstr "Descripcion" +#. success msgid "Done!" msgstr "Acabat !" @@ -107,10 +105,6 @@ msgid "Install a firmware file on this hardware" msgstr "Installar un fichièr de micrologicial sus aqueste material" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Installar immediatament las mesas a jorn preparadas" - msgid "Mode" msgstr "Mòde" @@ -148,10 +142,6 @@ msgid "Removed" msgstr "Suprimit" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Mostrar las informacions de desbugatge per totes los fichièrs" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Mostrar las opcions de desbugatge" diff -Nru fwupd-1.0.9/po/pl.po fwupd-1.2.10/po/pl.po --- fwupd-1.0.9/po/pl.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/pl.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,14 +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" -"POT-Creation-Date: 2018-06-06 14:54+0100\n" -"PO-Revision-Date: 2018-06-06 15:55+0000\n" -"Last-Translator: Piotr Drąg \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" @@ -18,11 +15,126 @@ "Language: pl\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Pozostała %.0f minuta" +msgstr[1] "Pozostały %.0f minuty" +msgstr[2] "Pozostało %.0f minut" +msgstr[3] "Pozostało %.0f minut" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Aktualizacja podsystemu ME użytkownika końcowego urządzenia %s" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Aktualizacja kontrolera %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "Aktualizacja firmowego podsystemu ME urządzenia %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Aktualizacja urządzenia %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Aktualizacja wbudowanego kontrolera %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Aktualizacja podsystemu ME urządzenia %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Aktualizacja komputera %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Aktualizacja urządzenia %s" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "Dostępne są aktualizacje oprogramowania sprzętowego dla urządzenia %s:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +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: command description +msgid "Activate devices" +msgstr "Aktywuje urządzenia" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktywuje oczekujące urządzenia" + +msgid "Activate the new firmware on the device" +msgstr "Aktywacja nowego oprogramowania sprzętowego na urządzeniu" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktywowanie aktualizacji oprogramowania sprzętowego" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktywowanie aktualizacji oprogramowania sprzętowego dla" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Dodano" @@ -48,10 +160,14 @@ 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" @@ -60,6 +176,19 @@ msgid "Apply a binary patch" msgstr "Zastosowuje binarną łatę" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Zastosowuje aktualizacje oprogramowania sprzętowego" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Zatwierdzone oprogramowanie sprzętowe:" +msgstr[1] "Zatwierdzone oprogramowanie sprzętowe:" +msgstr[2] "Zatwierdzone oprogramowanie sprzętowe:" +msgstr[3] "Zatwierdzone oprogramowanie sprzętowe:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Podłącza urządzenie DFU z powrotem do uruchamiania systemu" @@ -90,6 +219,22 @@ msgstr "Wymagane jest uwierzytelnienie, aby zmodyfikować skonfigurowane repozytorium używane do aktualizacji oprogramowania sprzętowego" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Wymagane jest uwierzytelnienie, aby zmodyfikować konfigurację usługi" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Wymagane jest uwierzytelnienie, aby ustawić listę zatwierdzonego oprogramowania sprzętowego" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Wymagane jest uwierzytelnienie, aby podpisać dane za pomocą certyfikatu klienta" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Wymagane jest uwierzytelnienie, aby przełączyć na nową wersję oprogramowania sprzętowego" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Wymagane jest uwierzytelnienie, aby odblokować urządzenie" @@ -194,6 +339,10 @@ msgid "Detach to bootloader mode" msgstr "Odłącza do trybu programu startowego" +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Identyfikator urządzenia" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Dodano urządzenie:" @@ -214,11 +363,18 @@ msgid "Devices that were not updated correctly:" msgstr "Urządzenia, które nie zostały poprawnie zaktualizowane:" +msgid "Disabled fwupdate debugging" +msgstr "Wyłączono debugowanie fwupdate" + #. TRANSLATORS: command description msgid "Disables a given remote" msgstr "Wyłącza podane repozytorium" #. TRANSLATORS: command line option +msgid "Display version" +msgstr "Wyświetla wersję" + +#. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Bez sprawdzania przestarzałych metadanych" @@ -234,6 +390,7 @@ msgid "Do not write to the history database" msgstr "Bez zapisywania do bazy danych historii" +#. success msgid "Done!" msgstr "Gotowe." @@ -248,6 +405,11 @@ 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…" @@ -264,6 +426,14 @@ msgid "Dump information about a binary patch to the screen" msgstr "Zrzuca informacje o binarnej łacie na ekran" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Podana partycja ESP nie jest prawidłowa" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Włącza obsługę aktualizacji oprogramowania sprzętowego na obsługiwanych systemach" + #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" msgstr "Włączyć to repozytorium?" @@ -272,10 +442,16 @@ msgid "Enabled" msgstr "Włączone" +msgid "Enabled fwupdate debugging" +msgstr "Włączono debugowanie fwupdate" + #. TRANSLATORS: command description msgid "Enables a given remote" msgstr "Włącza podane repozytorium" +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 "Włączenie tej funkcjonalności jest wykonywane na własne ryzyko, co oznacza, że należy skontaktować się z oryginalnym producentem sprzętu w sprawie ewentualnych problemów spowodowanych przez te aktualizacje. Tylko problemy z samym procesem aktualizacji powinny być zgłaszane pod adresem $OS_RELEASE:BUG_REPORT_URL$." + #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." msgstr "Włączenie tego repozytorium wykonywane jest na własne ryzyko." @@ -300,10 +476,22 @@ msgid "Exit after the engine has loaded" msgstr "Kończy działanie po wczytaniu mechanizmu" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Połączenie z usługą się nie powiodło" + #. TRANSLATORS: the server is rate-limiting downloads msgid "Failed to download due to server limit" msgstr "Pobranie się nie powiodło z powodu ograniczenia serwera" +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Uzyskanie oczekujących urządzeń się nie powiodło" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Zainstalowanie aktualizacji oprogramowania sprzętowego się nie powiodło" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Wczytanie poprawek się nie powiodło" @@ -312,6 +500,14 @@ msgid "Failed to parse arguments" msgstr "Przetworzenie parametrów się nie powiodło" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Ponowne uruchomienie się nie powiodło" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Ustawienie trybu ekranu wczytywania się nie powiodło" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Pobieranie pliku" @@ -336,6 +532,10 @@ msgid "Filename Signature" msgstr "Podpis nazwy pliku" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Agent oprogramowania sprzętowego" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "Podstawowy adres URI oprogramowania sprzętowego" @@ -361,6 +561,19 @@ msgstr[2] "Metadane oprogramowania sprzętowego nie zostały zaktualizowane przez %u dni i mogą nie być aktualne." msgstr[3] "Metadane oprogramowania sprzętowego nie zostały zaktualizowane przez %u dni i mogą nie być aktualne." +msgid "Firmware updates are not supported on this machine." +msgstr "Aktualizacje oprogramowania sprzętowego nie są obsługiwane na tym komputerze." + +msgid "Firmware updates are supported on this machine." +msgstr "Aktualizacje oprogramowania sprzętowego są obsługiwane na tym komputerze." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Flagi" + +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" @@ -373,6 +586,10 @@ msgstr "Uzyskuje wszystkie urządzenia zgodnie z topologią komputera" #. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Uzyskuje wszystkie urządzenia i możliwe wydania" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Uzyskuje wszystkie urządzenia obsługujące aktualizacje oprogramowania sprzętowego" @@ -392,6 +609,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Uzyskuje kryptograficzną sumę kontrolną zrzuconego oprogramowania sprzętowego" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Uzyskuje listę zatwierdzonego oprogramowania sprzętowego." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Uzyskuje listę aktualizacji dla podłączonego sprzętu" @@ -423,10 +644,6 @@ msgid "Install old version of system firmware" msgstr "Instalacja poprzedniej wersji oprogramowania sprzętowego komputera" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Instaluje przygotowaną aktualizację teraz" - msgid "Install signed device firmware" msgstr "Instalacja podpisanego oprogramowania sprzętowego urządzenia" @@ -439,13 +656,26 @@ msgid "Install unsigned system firmware" msgstr "Instalacja niepodpisanego oprogramowania sprzętowego komputera" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalowanie oprogramowania sprzętowego…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Instalowanie aktualizacji oprogramowania sprzętowego…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instalowanie na urządzeniu %s…" + msgid "Keyring" msgstr "Baza kluczy" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Pozostała mniej niż jedna minuta" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (stabilne oprogramowanie sprzętowe)" @@ -456,6 +686,10 @@ msgid "List currently attached DFU capable devices" msgstr "Wyświetla listę obecnie podłączonych urządzeń DFU" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Wyświetla listę obsługiwanych aktualizacji oprogramowania sprzętowego" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Wczytywanie…" @@ -480,9 +714,18 @@ msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadane można uzyskać z serwisu Linux Vendor Firmware Service." +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Niezgodna usługa i klient, proszę użyć %s zamiast tego" + msgid "Mode" msgstr "Tryb" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Modyfikuje wartość konfiguracji usługi." + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Modyfikuje podane repozytorium" @@ -490,6 +733,9 @@ msgid "Modify a configured remote" msgstr "Modyfikacja skonfigurowanego repozytorium" +msgid "Modify daemon configuration" +msgstr "Modyfikacja konfiguracji usługi" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Monitoruje zdarzenia usługi" @@ -500,6 +746,9 @@ msgid "Name" msgstr "Nazwa" +msgid "No action specified!" +msgstr "Nie podano działania." + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" @@ -513,6 +762,10 @@ msgid "No remotes are currently enabled so no metadata is available." msgstr "Żadne repozytoria nie są obecnie włączone, więc żadne metadane nie są dostępne." +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nie zastosowano żadnych aktualizacji" + msgid "OK" msgstr "OK" @@ -520,6 +773,14 @@ msgid "Override plugin warning" msgstr "Zastępuje ostrzeżenie wtyczki" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Zastępuje domyślną ścieżkę ESP" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Zastępuje ostrzeżenia i wymusza działanie" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Hasło" @@ -527,6 +788,10 @@ msgid "Payload" msgstr "Dane" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Procent ukończenia" + msgid "Permission denied" msgstr "Brak uprawnień" @@ -535,6 +800,12 @@ 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" @@ -546,6 +817,10 @@ msgid "Protocol" msgstr "Protokół" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Odpytuje obsługę aktualizacji oprogramowania sprzętowego" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -563,6 +838,10 @@ msgid "Reading…" msgstr "Odczytywanie…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Ponowne uruchamianie…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Odświeża metadane ze zdalnego serwera" @@ -610,6 +889,10 @@ msgid "Restart now?" msgstr "Uruchomić ponownie?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Uruchomić usługę ponownie, aby uwzględnić zmianę?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Ponowne uruchamianie urządzenia…" @@ -618,10 +901,22 @@ msgid "Return all the hardware IDs for the machine" msgstr "Zwraca wszystkie identyfikatory sprzętu dla komputera" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Wykonuje procedurę czyszczenia składania wtyczki podczas używania „install-blob”" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Wykonuje procedurę przygotowania składania wtyczki podczas używania „install-blob”" + msgid "Runtime" msgstr "Uruchamianie systemu" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Zapisuje stan urządzenia do pliku JSON między wykonaniami" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Planuje instalację podczas następnego ponownego uruchomienia" @@ -653,18 +948,29 @@ msgid "Set release version on firmware file" msgstr "Ustawia wersję wydania pliku oprogramowania sprzętowego" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Ustawia flagę debugowania podczas aktualizacji" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Ustawia rozmiar oprogramowania sprzętowego dla celu" #. TRANSLATORS: command description msgid "Set vendor ID on firmware file" -msgstr "Ustawia identyfikator producenta pliku oprogramowania sprzętowego" +msgstr "Ustawia identyfikator dostawcy pliku oprogramowania sprzętowego" #. TRANSLATORS: command description msgid "Sets metadata on a firmware file" msgstr "Ustawia metadane pliku oprogramowania sprzętowego" +msgid "Sets the list of approved firmware" +msgstr "Ustawienie listy zatwierdzonego oprogramowania sprzętowego" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Ustawia listę zatwierdzonego oprogramowania sprzętowego." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Udostępnia historię oprogramowania sprzętowego programistom" @@ -673,9 +979,13 @@ 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 for a particular domain" +msgstr "Wyświetla więcej informacji o usłudze dla konkretnej domeny" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Wyświetla informacje o debugowaniu dla wszystkich plików" +msgid "Show debugging information for all domains" +msgstr "Wyświetla informacje o debugowaniu dla wszystkich domen" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" @@ -697,6 +1007,38 @@ msgid "Show plugin verbose information" msgstr "Wyświetla więcej informacji o wtyczkach" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Wyświetla dziennik debugowania z ostatniej aktualizacji" + +#. TRANSLATORS: command line option +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 "Sign data using the client certificate" +msgstr "Podpisanie danych za pomocą certyfikatu klienta" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Podpisanie danych za pomocą certyfikatu klienta" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Podpisuje wysłane dane za pomocą certyfikatu klienta" + +msgid "Signature" +msgstr "Podpis" + +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" @@ -714,15 +1056,25 @@ msgid "Target" msgstr "Cel" -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: 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 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." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Nie ma zatwierdzonego oprogramowania sprzętowego." #. 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." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "To narzędzie może być używane tylko przez użytkownika root" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -736,6 +1088,10 @@ msgid "Type" msgstr "Typ" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Narzędzie oprogramowania sprzętowego UEFI" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "Adres URI" @@ -751,6 +1107,15 @@ msgid "Unlocks the device for firmware access" msgstr "Odblokowuje urządzenie" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Usuwa flagę debugowania podczas aktualizacji" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Nieobsługiwana wersja usługi %s, wersja klienta to %s" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Suma kontrolna aktualizacji" @@ -759,6 +1124,11 @@ 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" @@ -779,8 +1149,9 @@ 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:" @@ -798,6 +1169,10 @@ msgstr "Aktualizuje przechowywane metadane bieżącą zawartością pamięci ROM" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Aktualizuje przechowywane metadane obecną zawartością" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Aktualizuje całe oprogramowanie sprzętowe do najnowszych dostępnych wersji" @@ -808,6 +1183,11 @@ 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:" @@ -859,6 +1239,3 @@ #. 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 -Nru fwupd-1.0.9/po/POTFILES.in fwupd-1.2.10/po/POTFILES.in --- fwupd-1.0.9/po/POTFILES.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/POTFILES.in 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,14 @@ -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 plugins/dfu/dfu-tool.c plugins/uefi/fu-plugin-uefi.c +plugins/uefi/fu-uefi-tool.c +src/fu-agent.c src/fu-config.c src/fu-debug.c src/fu-main.c +src/fu-offline.c src/fu-tool.c src/fu-progressbar.c src/fu-util.c diff -Nru fwupd-1.0.9/po/POTFILES.skip fwupd-1.2.10/po/POTFILES.skip --- fwupd-1.0.9/po/POTFILES.skip 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/po/POTFILES.skip 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1 @@ +data/org.freedesktop.fwupd.metainfo.xml diff -Nru fwupd-1.0.9/po/pt_BR.po fwupd-1.2.10/po/pt_BR.po --- fwupd-1.0.9/po/pt_BR.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/pt_BR.po 2019-07-15 18:25:54.000000000 +0000 @@ -6,15 +6,12 @@ # Derek W. Stavis , 2015 # Derek W. Stavis , 2016 # Derek W. Stavis , 2015-2016 -# Rafael Fontenelle , 2017 -# Rafael Fontenelle , 2015-2018 +# Rafael Fontenelle , 2017-2018 +# Rafael Fontenelle , 2015-2019 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 22:56+0000\n" -"Last-Translator: Rafael Fontenelle \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" @@ -22,11 +19,116 @@ "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: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Atualização consumidor-final de ME para %s " + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Atualização do controlador %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "Atualização corporativa de ME para %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Atualização do dispositivo %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Atualização do controlador embarcado %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Atualização do ME %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Atualização do sistema %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Atualização de %s" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s tem atualizações de firmware:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +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: command description +msgid "Activate devices" +msgstr "Ativa dispositivos" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Ativa dispositivos pendentes" + +msgid "Activate the new firmware on the device" +msgstr "Ativar o novo firmware no dispositivo" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Ativando atualização de firmware" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Ativando atualização de firmware para" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Adicionado" @@ -46,16 +148,20 @@ #. 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." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Uma atualização requer que o sistema seja desligado por completo." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Responde sim para todas as perguntas" @@ -64,6 +170,17 @@ msgid "Apply a binary patch" msgstr "Aplica um patch binário" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplica atualizações de firmware" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Firmware aprovado:" +msgstr[1] "Firmwares aprovados:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Anexa um dispositivo com capacidade de DFU em tempo real" @@ -83,17 +200,33 @@ #. 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" msgstr "É requerida autenticação para modificar um remoto configurado usado para atualizações de firmware" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "É requerida autenticação para modificar uma configuração de daemon" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "É requirida autenticação para definir a lista de firmwares aprovados" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "É requirida autenticação para assinar dados usando o certificado client" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "É requirida autenticação para alternar para nova versão de firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "É requerida autenticação para desbloquear um dispositivo" @@ -198,6 +331,10 @@ msgid "Detach to bootloader mode" msgstr "Modo desanexar ao gerenciador de boot" +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "ID do dispositivo" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Dispositivo adicionado:" @@ -218,11 +355,18 @@ msgid "Devices that were not updated correctly:" msgstr "Dispositivos que não foram atualizados com sucesso:" +msgid "Disabled fwupdate debugging" +msgstr "Depuração de fwupdate desabilitada" + #. TRANSLATORS: command description msgid "Disables a given remote" msgstr "Desabilita um remoto dado" #. TRANSLATORS: command line option +msgid "Display version" +msgstr "Exibe a versão" + +#. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Não verifica por metadados antigos" @@ -238,19 +382,25 @@ msgid "Do not write to the history database" msgstr "Não escreve no banco de dados de histórico" +#. success msgid "Done!" 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…" @@ -268,6 +418,14 @@ msgid "Dump information about a binary patch to the screen" msgstr "Despeja informações sobre um patch binário na tela" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "O ESP especificado não era válido" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Habilita suporte a atualização de firmware em sistemas suportados" + #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" msgstr "Habilitar esse remoto?" @@ -276,10 +434,16 @@ msgid "Enabled" msgstr "Habilitado" +msgid "Enabled fwupdate debugging" +msgstr "Depuração de fwupdate habilitada" + #. TRANSLATORS: command description msgid "Enables a given remote" msgstr "Habilita um remoto dado" +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 "A habilitação dessa funcionalidade é feita por sua conta e risco, o que significa que você precisa entrar em contato com o fabricante do equipamento original sobre quaisquer problemas causados por essas atualizações. Somente problemas com o processo de atualização devem ser relatados em $ OS_RELEASE: BUG_REPORT_URL $." + #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." msgstr "A habilitação deste remoto é feito a seu próprio custo e risco." @@ -304,10 +468,22 @@ msgid "Exit after the engine has loaded" msgstr "Sair após o carregamento do motor" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Falha ao se conectar ao daemon" + #. TRANSLATORS: the server is rate-limiting downloads msgid "Failed to download due to server limit" msgstr "Falha ao baixar em razão de limite do servidor" +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Falha ao obter dispositivos pendentes" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Falha ao instalar a atualização de firmware" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Falha ao carregar peculiaridades" @@ -316,6 +492,14 @@ msgid "Failed to parse arguments" msgstr "Falha ao interpretar argumentos" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Falha ao reinicializar" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Falha ao definir o modo splash" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Obtendo arquivo" @@ -340,6 +524,10 @@ msgid "Filename Signature" msgstr "Assinatura de nome de arquivo" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Agente de firmware" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "URI base de firmware" @@ -363,6 +551,19 @@ msgstr[0] "Os metadados do firmware não foram atualizados a %u dia e podem não estar atualizados." msgstr[1] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." +msgid "Firmware updates are not supported on this machine." +msgstr "Não há suporte a atualizações de firmware nessa máquina." + +msgid "Firmware updates are supported on this machine." +msgstr "Há suporte a atualizações de firmware nesta máquina." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Sinalizadores" + +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" @@ -375,6 +576,10 @@ msgstr "Obtém todos os dispositivos de acordo com a topologia do sistema" #. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Obtém todos os dispositivos e lançamentos possíveis" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Obtém todos os dispositivos que oferecem suporte às atualizações de firmware" @@ -394,6 +599,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Obtém o hash criptográfico do firmware despejado" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Obtém a lista de firmwares aprovados." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Obtém a lista de atualizações para os hardwares conectados" @@ -425,10 +634,6 @@ msgid "Install old version of system firmware" msgstr "Instalar versão antiga do firmware no sistema" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Instala as atualizações preparadas agora" - msgid "Install signed device firmware" msgstr "Instalar firmware assinado no dispositivo" @@ -441,13 +646,26 @@ msgid "Install unsigned system firmware" msgstr "Instalar firmware não assinado no sistema" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalando o firmware…" + #. 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)" @@ -458,6 +676,10 @@ msgid "List currently attached DFU capable devices" msgstr "Lista os dispositivos atualmente anexados com capacidade de DFU" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Lista atualizações de firmware suportadas" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Carregando…" @@ -482,9 +704,18 @@ msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadados podem ser obtidos do Linux Vendor Firmware Service." +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Daemon e cliente incompatíveis, use %s" + msgid "Mode" msgstr "Modo" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Modifica o valor de uma configuração do daemon." + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Modifica um remoto dado" @@ -492,6 +723,9 @@ msgid "Modify a configured remote" msgstr "Modificar um remoto configurado" +msgid "Modify daemon configuration" +msgstr "Modificar a configuração do daemon" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Monitora o daemon por eventos" @@ -502,6 +736,9 @@ msgid "Name" msgstr "Nome" +msgid "No action specified!" +msgstr "Nenhuma ação especificada!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" @@ -515,6 +752,10 @@ msgid "No remotes are currently enabled so no metadata is available." msgstr "Nenhum remoto está atualmente habilitado, então nenhum metadado está disponível." +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nenhuma atualização foi aplicada" + msgid "OK" msgstr "OK" @@ -522,6 +763,14 @@ msgid "Override plugin warning" msgstr "Sobrepõe um aviso de plugin" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Substitui o caminho ESP padrão" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Anula avisos e força a ação" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Senha" @@ -529,6 +778,10 @@ msgid "Payload" msgstr "Carga" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentagem concluída" + msgid "Permission denied" msgstr "Permissão negada" @@ -537,6 +790,12 @@ 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" @@ -548,6 +807,10 @@ msgid "Protocol" msgstr "Protocolo" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Consulta por suporte a atualização de firmware" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -565,6 +828,10 @@ msgid "Reading…" msgstr "Lendo…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Reinicializando…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Renova metadados do servidor remoto" @@ -612,6 +879,10 @@ msgid "Restart now?" msgstr "Reiniciar agora?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Reiniciar o daemon para tornar a mudança efetiva?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Reiniciando dispositivo…" @@ -620,10 +891,22 @@ msgid "Return all the hardware IDs for the machine" msgstr "Retorna todos os IDs de hardware para a máquina" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Executa a rotina de limpeza da composição de plugin ao usar install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Executa a rotina de preparação da composição de plugin ao usar install-blob" + msgid "Runtime" msgstr "Runtime" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Salva o estado do dispositivo em um arquivo JSON entre execuções" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Agenda instalação para próxima reinicialização quando possível" @@ -655,6 +938,10 @@ msgid "Set release version on firmware file" msgstr "Define a versão de lançamento no arquivo de firmware" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Define a opção de depuração durante atualização" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Define o tamanho do firmware para o alvo" @@ -667,6 +954,13 @@ msgid "Sets metadata on a firmware file" msgstr "Define os metadados em um arquivo de firmware" +msgid "Sets the list of approved firmware" +msgstr "Define a lista de firmwares aprovados" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Define a lista de firmware aprovados." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Compartilha histórico de firmware com os desenvolvedores" @@ -675,9 +969,13 @@ 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 for a particular domain" +msgstr "Mostrar informações detalhadas do daemon para um domínio em particular" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Mostrar informações de depuração para todos os arquivos" +msgid "Show debugging information for all domains" +msgstr "Mostrar informações de depuração para todos domínios" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" @@ -699,6 +997,38 @@ msgid "Show plugin verbose information" msgstr "Mostra informação verbosa de plugin" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Mostra o registro log de depuração da tentativa mais recente de atualização" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Mostra as informações do status de atualização de firmware" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Desligar agora?" + +msgid "Sign data using the client certificate" +msgstr "Assina dados usando o certificado cliente" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Assina dados usando o certificado cliente" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Assina os dados enviados com o certificado cliente" + +msgid "Signature" +msgstr "Assinatura" + +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" @@ -716,15 +1046,25 @@ msgid "Target" msgstr "Alvo" -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: 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 "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. Todo firmware é fornecido apenas pelo fabricante do equipamento original." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Nenhum firmware aprovado." #. 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." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Essa ferramenta só pode ser usada pelo usuário root" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -738,6 +1078,10 @@ msgid "Type" msgstr "Tipo" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitário de Firmware UEFI" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -753,6 +1097,15 @@ msgid "Unlocks the device for firmware access" msgstr "Desbloqueia o dispositivo para acesso do firmware" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Desativa a opção de depuração durante atualização" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Sem suporte ao daemon na versão %s, a versão do cliente é %s" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Soma de verificação da atualização" @@ -761,6 +1114,11 @@ 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" @@ -781,8 +1139,9 @@ msgid "Update Version" msgstr "Versão da atualização" -msgid "Update device firmware on Linux" -msgstr "Atualize firmware de dispositivos no Linux" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Atualiza todos os dispositivos que correspondem aos metadados locais" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -800,6 +1159,10 @@ msgstr "Atualiza os metadados armazenados com o conteúdo da ROM atual" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Atualiza os metadados armazenados com o conteúdo atual" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Atualiza todos os firmwares para a última versão disponível" @@ -810,6 +1173,11 @@ 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:" @@ -861,6 +1229,3 @@ #. 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 -Nru fwupd-1.0.9/po/ru.po fwupd-1.2.10/po/ru.po --- fwupd-1.0.9/po/ru.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/ru.po 2019-07-15 18:25:54.000000000 +0000 @@ -4,14 +4,11 @@ # # Translators: # Igor , 2017 -# Serge Vylekzhanin , 2015-2018 +# Serge Vylekzhanin , 2015-2019 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: Russian (http://www.transifex.com/freedesktop/fwupd/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,10 +16,125 @@ "Language: ru\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Осталась %.0f минута" +msgstr[1] "Осталось %.0f минуты" +msgstr[2] "Осталось %.0f минут" +msgstr[3] "Осталось %.0f минут" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Обновление подсистемы Consumer МЕ устройства %s" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Обновление контроллера %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "Обновление подсистемы Corporate МЕ устройства %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Обновление устройства %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Обновление встроенного контроллера %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Обновление подсистемы МЕ устройства %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Обновление системы %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Обновление устройства %s" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" -msgstr "У %s есть обновления микропрограммы:" +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: command description +msgid "Activate devices" +msgstr "Активировать устройства" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Активировать ожидающие устройства" + +msgid "Activate the new firmware on the device" +msgstr "Активировать новую прошивку на устройстве" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Активация обновления прошивки" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Активация обновления прошивки для" #. TRANSLATORS: this is when a device is hotplugged msgid "Added" @@ -32,6 +144,10 @@ msgid "Age" msgstr "Возраст" +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Согласиться и активировать репозиторий?" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" @@ -39,20 +155,49 @@ #. TRANSLATORS: command line option msgid "Allow downgrading firmware versions" -msgstr "Разрешить понижение версий микропрограмм" +msgstr "Разрешить понижение версий прошивок" #. TRANSLATORS: command line option msgid "Allow re-installing existing firmware versions" -msgstr "Разрешить повторную установку существующих версий микропрограмм" +msgstr "Разрешить повторную установку существующих версий прошивок" + +#. 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 "Ответить да на все вопросы" #. TRANSLATORS: command description msgid "Apply a binary patch" msgstr "Применить бинарный патч" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Применить обновления прошивки" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Одобренная прошивка:" +msgstr[1] "Одобренная прошивка:" +msgstr[2] "Одобренная прошивка:" +msgstr[3] "Одобренная прошивка:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Вернуть устройство с возможностями DFU к использованию" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Подключить в режим прошивки" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -64,15 +209,31 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "Для понижения версии микропрограммы на съёмном устройстве требуется аутентификация" +msgstr "Для понижения версии прошивки на съёмном устройстве требуется аутентификация" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" -msgstr "Для понижения версии микропрограммы на этой машине требуется аутентификация" +msgstr "Для понижения версии прошивки на этой машине требуется аутентификация" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" -msgstr "Для модификации настроенного дистанционного устройства, используемого для обновления микропрограммы, необходима аутентификация" +msgstr "Для модификации настроенного репозитория, используемого для обновления прошивки, требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Для изменения настроек фоновой службы требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Для установки списка одобренных прошивок требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Для подписи данных с использованием сертификата клиента требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Для переключения на новую версию прошивки требуется аутентификация" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" @@ -80,11 +241,11 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on a removable device" -msgstr "Для обновления микропрограммы на съёмном устройстве требуется аутентификация" +msgstr "Для обновления прошивки на съёмном устройстве требуется аутентификация" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" -msgstr "Для обновления микропрограммы на этой машине требуется аутентификация" +msgstr "Для обновления прошивки на этой машине требуется аутентификация" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the stored checksums for the device" @@ -92,7 +253,11 @@ #. TRANSLATORS: command description msgid "Build firmware using a sandbox" -msgstr "Собрать микропрограмму с помощью песочницы" +msgstr "Собрать прошивку, используя песочницу" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Отменить" #. TRANSLATORS: this is when a device ctrl+c's a watch msgid "Cancelled" @@ -110,7 +275,7 @@ #. TRANSLATORS: chip ID, e.g. "0x58200204" msgid "Chip ID" -msgstr "ID чипа" +msgstr "Идентификатор чипа" #. TRANSLATORS: get interactive prompt msgid "Choose a device:" @@ -126,11 +291,11 @@ #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" -msgstr "Очищает все обновления, которые запланированы для автономного режима" +msgstr "Очистить все обновления, которые запланированы для автономного режима" #. TRANSLATORS: command description msgid "Clears the results from the last update" -msgstr "Очищает результаты c последнего обновления" +msgstr "Очистить результаты c последнего обновления" #. TRANSLATORS: error message msgid "Command not found" @@ -138,12 +303,15 @@ #. TRANSLATORS: command description msgid "Convert firmware to DFU format" -msgstr "Преобразовать микропрограмму в формат DFU" +msgstr "Преобразовать прошивку в формат DFU" #. TRANSLATORS: command description msgid "Create a binary patch using two files" msgstr "Создать бинарный патч на основе двух файлов" +msgid "DFU" +msgstr "DFU" + #. TRANSLATORS: DFU stands for device firmware update msgid "DFU Utility" msgstr "Средство работы с DFU" @@ -158,7 +326,7 @@ #. TRANSLATORS: command description msgid "Decrypt firmware data" -msgstr "Расшифровать данные микропрограммы" +msgstr "Расшифровать данные прошивки" #. TRANSLATORS: section header for firmware description msgid "Description" @@ -168,6 +336,14 @@ msgid "Detach currently attached DFU capable device" msgstr "Отсоединить подсоединённое сейчас устройство с возможностями DFU" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Отключить в режим загрузчика" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Идентификатор устройства" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Добавлено устройство:" @@ -178,21 +354,62 @@ #. TRANSLATORS: this is when a device is hotplugged msgid "Device removed:" -msgstr "Изъято устройство:" +msgstr "Удалено устройство:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Устройства, которые были успешно обновлены:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Устройства, которые не были правильно обновлены:" + +msgid "Disabled fwupdate debugging" +msgstr "Отладка fwupdate деактивирована" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Деактивировать данный репозиторий" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Показать версию" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Не проверять старые метаданные" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Не проверять перезагрузку после обновления" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Не проверять незарегистрированную историю" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Не сохранять в базу данных истории" +#. success msgid "Done!" msgstr "Готово!" #. TRANSLATORS: command description msgid "Downgrades the firmware on a device" -msgstr "Скачивает микропрограмму на устройство" +msgstr "Понизить версию прошивки на устройстве" #. 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 "Понижение версии %s с %s на %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…" @@ -204,19 +421,49 @@ #. TRANSLATORS: command description msgid "Dump details about a firmware file" -msgstr "Создать дамп данных по файлу микропрограммы" +msgstr "Создать дамп подробных данных о файле прошивки" #. TRANSLATORS: command description msgid "Dump information about a binary patch to the screen" msgstr "Вывести дамп информации о бинарном патче на экран" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Указанный раздел ESP недействителен" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Активировать поддержку обновлений прошивки в поддерживаемых системах." + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Активировать этот репозиторий?" + #. TRANSLATORS: if the remote is enabled msgid "Enabled" -msgstr "Включено" +msgstr "Активировано" + +msgid "Enabled fwupdate debugging" +msgstr "Отладка fwupdate активирована" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Активировать данный репозиторий" + +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "Активирование этого функционала осуществляется на ваш страх и риск. Это означает, что вам следует обратиться к производителю оборудования относительно любых проблем, вызванных этими обновлениями. Только проблемы с фактическим процессом обновления следует сообщать в $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Активация этого репозитория осуществляется на свой страх и риск." #. TRANSLATORS: command description msgid "Encrypt firmware data" -msgstr "Зашифровать данные микропрограммы" +msgstr "Зашифровать данные прошивки" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Стереть всю историю обновлений прошивки" #. TRANSLATORS: erasing contents of the flash chips msgid "Erasing…" @@ -230,17 +477,45 @@ msgid "Exit after the engine has loaded" msgstr "Выйти после загрузки движка" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Не удалось подключиться к фоновой службе" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "Не удалось загрузить из-за ограничения сервера" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Не удалось получить ожидающие устройства" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Не удалось установить обновление прошивки" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Не удалось загрузить странности" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Не удалось разобрать аргументы" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Не удалось перезагрузить" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Не удалось установить режим заставки" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Получение файла" #. TRANSLATORS: downloading new firmware file msgid "Fetching firmware" -msgstr "Получение микропрограммы" +msgstr "Получение прошивки" #. TRANSLATORS: downloading new metadata file msgid "Fetching metadata" @@ -258,21 +533,47 @@ msgid "Filename Signature" msgstr "Подпись имени файла" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Агент прошивки" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" -msgstr "База URI микропрограммы" +msgstr "Базовый URI прошивки" #. TRANSLATORS: program summary msgid "Firmware Update D-Bus Service" -msgstr "D-Bus служба обновления микропрограммы" +msgstr "D-Bus служба обновления прошивки" #. TRANSLATORS: program name msgid "Firmware Update Daemon" -msgstr "Служба обновления микропрограммы" +msgstr "Фоновая служба обновления прошивки" #. TRANSLATORS: program name msgid "Firmware Utility" -msgstr "Средство работы с микропрограммами" +msgstr "Средство работы с прошивками" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "Метаданные прошивки не обновлялись в течение %u дня и могут быть устаревшими." +msgstr[1] "Метаданные прошивки не обновлялись в течение %u дней и могут быть устаревшими." +msgstr[2] "Метаданные прошивки не обновлялись в течение %u дней и могут быть устаревшими." +msgstr[3] "Метаданные прошивки не обновлялись в течение %u дней и могут быть устаревшими." + +msgid "Firmware updates are not supported on this machine." +msgstr "Обновления прошивки не поддерживаются на этой машине." + +msgid "Firmware updates are supported on this machine." +msgstr "Обновления прошивки поддерживаются на этой машине." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Флаги" + +msgid "Force the action ignoring all warnings" +msgstr "Выполнить действие, игнорируя все предупреждения" #. TRANSLATORS: detected a DFU device msgid "Found" @@ -282,32 +583,48 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "Получить все устройства в соответствии с топологией системы" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Получить все устройства и возможные выпуски" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" -msgstr "Получить все устройства, которые поддерживают обновления микропрограммы" +msgstr "Получить все устройства, которые поддерживают обновления прошивки" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Получить все активированные плагины, зарегистрированные в системе" #. TRANSLATORS: command description msgid "Gets details about a firmware file" -msgstr "Получает сведения о файле микропрограммы" +msgstr "Получить сведения о файле прошивки" #. TRANSLATORS: command description msgid "Gets the configured remotes" -msgstr "Получает настроенные дистанционные устройства" +msgstr "Получить настроенные репозитории" #. TRANSLATORS: command description msgid "Gets the cryptographic hash of the dumped firmware" -msgstr "Получает криптографической хэш дампа микропрограммы" +msgstr "Получить криптографической хэш дампа прошивки" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Получить список одобренных прошивок." #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" -msgstr "Получает список обновлений для подключенного оборудования" +msgstr "Получить список обновлений для подключенного оборудования" #. TRANSLATORS: command description msgid "Gets the releases for a device" -msgstr "Получает релизы для устройства" +msgstr "Получить релизы для устройства" #. TRANSLATORS: command description msgid "Gets the results from the last update" -msgstr "Получает результаты с последнего обновления" +msgstr "Получить результаты с последнего обновления" #. TRANSLATORS: Appstream ID for the hardware type msgid "ID" @@ -318,46 +635,73 @@ msgstr "Бездействие…" #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Установить двоичную прошивку на устройство" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" -msgstr "Установить файл микропрограммы на это оборудование" +msgstr "Установить файл прошивки на это оборудование" msgid "Install old version of system firmware" -msgstr "Установить старую версию системной микропрограммы" - -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Установить подготовленные обновления сейчас" +msgstr "Установить старую версию системной прошивки" msgid "Install signed device firmware" -msgstr "Установить подписанную микропрограмму устройства" +msgstr "Установить подписанную прошивку устройства" msgid "Install signed system firmware" -msgstr "Установить подписанную системную микропрограмму" +msgstr "Установить подписанную системную прошивку" msgid "Install unsigned device firmware" -msgstr "Установить неподписанную микропрограмму устройства" +msgstr "Установить неподписанную прошивку устройства" msgid "Install unsigned system firmware" -msgstr "Установить неподписанную системную микропрограмму" +msgstr "Установить неподписанную системную прошивку" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Установка прошивки…" #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" -msgstr "Установка обновления микропрограммы..." +msgstr "Установка обновления прошивки…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Установка на устройство %s…" msgid "Keyring" msgstr "Хранилище ключей" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Осталось меньше минуты" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (стабильная прошивка)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (тестовая прошивка)" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "Вывести список подсоединённых сейчас устройств с возможностями DFU" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Вывести список поддерживаемых обновлений прошивки" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Загрузка…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "Изменить вручную белый список определённых плагинов" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" -msgstr "Объединить несколько файлов микропрограмм в один" +msgstr "Объединить несколько файлов прошивок в один файл" #. TRANSLATORS: remote URI msgid "Metadata URI" @@ -367,15 +711,31 @@ msgid "Metadata URI Signature" msgstr "Подпись URI метаданных" +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Метаданные можно получить в Linux Vendor Firmware Service." + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Несовместимые фоновая служба и клиент, используйте %s" + msgid "Mode" msgstr "Режим" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Изменить значение в конфигурации фоновой службы." + #. TRANSLATORS: command description msgid "Modifies a given remote" -msgstr "Модифицирует данное дистанционное устройство" +msgstr "Модифицировать данный репозиторий" msgid "Modify a configured remote" -msgstr "Изменить настроенное дистанционное устройство" +msgstr "Изменить настроенный репозиторий" + +msgid "Modify daemon configuration" +msgstr "Изменить настройки фоновой службы" #. TRANSLATORS: command description msgid "Monitor the daemon for events" @@ -387,50 +747,102 @@ msgid "Name" msgstr "Наименование" +msgid "No action specified!" +msgstr "Не определено никаких действий." + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" -msgstr "Не обнаружено оборудования с возможностью обновления микропрограммы" +msgstr "Не обнаружено оборудования с возможностью обновления прошивки" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Плагины не найдены" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "В настоящее время репозитории не активированы, поэтому метаданные недоступны." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Обновления не были применены" msgid "OK" msgstr "ОК" #. TRANSLATORS: command line option msgid "Override plugin warning" -msgstr "Переопределить предупреждение приложения" +msgstr "Переопределить предупреждение плагина" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Переопределить путь ESP по умолчанию" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Отменить предупреждения и выполнить действие" #. TRANSLATORS: remote filename base msgid "Password" msgstr "Пароль" +msgid "Payload" +msgstr "Полезная нагрузка" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Процент завершения" + msgid "Permission denied" msgstr "Доступ запрещен" +#. TRANSLATORS: the user isn't reading the question +#, c-format +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 "Приоритет" +msgid "Proceed with upload?" +msgstr "Продолжить загрузку?" + #. TRANSLATORS: DFU protocol version, e.g. 1.1 msgid "Protocol" msgstr "Протокол" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Запросить поддержку обновления прошивки" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" -msgstr "Проблемы" +msgstr "Странности" #. TRANSLATORS: command description msgid "Read firmware from device into a file" -msgstr "Считать микропрограмму из устройства файл" +msgstr "Считать прошивку из устройства в файл" #. TRANSLATORS: command description msgid "Read firmware from one partition into a file" -msgstr "Считать микропрограмму из одного раздела в файл" +msgstr "Считать прошивку из одного раздела в файл" #. TRANSLATORS: reading from the flash chips msgid "Reading…" msgstr "Чтение…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Перезагрузка…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Обновить метаданные с удаленного сервера" @@ -448,11 +860,11 @@ #. TRANSLATORS: section header for the remote the file is coming from msgid "Remote" -msgstr "Дистанционное устройство" +msgstr "Репозиторий" #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" -msgstr "Удалённый ID" +msgstr "Идентификатор репозитория" #. TRANSLATORS: this is when a device is hotplugged msgid "Removed" @@ -460,23 +872,54 @@ #. TRANSLATORS: command description msgid "Replace data in an existing firmware file" -msgstr "Заменить данные в существующем файле микропрограммы" +msgstr "Заменить данные в существующем файле прошивки" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI для отчета" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Требуется подключение к сети Интернет" #. TRANSLATORS: command description msgid "Reset a DFU device" msgstr "Восстановить исходное состояние устройства DFU" +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Перезагрузить сейчас?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Перезапустить фоновую службу, чтобы изменения вступили в силу?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Перезапуск устройства…" #. TRANSLATORS: command description msgid "Return all the hardware IDs for the machine" -msgstr "Показать ID всех устройств на машине" +msgstr "Показать идентификаторы всех устройств на машине" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Запустить процедуру очистки составного плагина, используя двоичную установку" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Запустить процедуру подготовки составного плагина, используя двоичную установку" + +msgid "Runtime" +msgstr "Время выполнения" + +#. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Сохранить состояние устройства в файл JSON между выполнениями" #. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" -msgstr "Планирование установки на следующую перезагрузку, если это возможно" +msgstr "Запланировать установку на следующую перезагрузку, если это возможно" #. TRANSLATORS: scheduing an update to be done on the next boot msgid "Scheduling…" @@ -488,55 +931,114 @@ #. TRANSLATORS: command description msgid "Set alternative name on firmware file" -msgstr "Установить альтернативное наименование для файла микропрограммы" +msgstr "Установить альтернативное наименование для файла прошивки" #. TRANSLATORS: command description msgid "Set alternative number on firmware file" -msgstr "Установить альтернативный номер для файла микропрограммы" +msgstr "Установить альтернативный номер для файла прошивки" #. TRANSLATORS: command description msgid "Set element address on firmware file" -msgstr "Установить адрес элемента для файла микропрограммы" +msgstr "Установить адрес элемента для файла прошивки" #. TRANSLATORS: command description msgid "Set product ID on firmware file" -msgstr "Установить идентификатор продукта для файла микропрограммы" +msgstr "Установить идентификатор продукта для файла прошивки" #. TRANSLATORS: command description msgid "Set release version on firmware file" -msgstr "Установить версию выпуска для файла микропрограммы" +msgstr "Установить версию выпуска для файла прошивки" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Установить флаг отладки во время обновления" #. TRANSLATORS: command description msgid "Set the firmware size for the target" -msgstr "Установить размер микропрограммы для конечного объекта" +msgstr "Установить размер прошивки для конечного объекта" #. TRANSLATORS: command description msgid "Set vendor ID on firmware file" -msgstr "Установить идентификатор производителя для файла микропрограммы" +msgstr "Установить идентификатор производителя для файла прошивки" #. TRANSLATORS: command description msgid "Sets metadata on a firmware file" -msgstr "Устанавливает метаданные файла микропрограммы" +msgstr "Установить метаданные файла прошивки" + +msgid "Sets the list of approved firmware" +msgstr "Установить список одобренных прошивок" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Установить список одобренных прошивок." + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Поделиться историей прошивки с разработчиками" #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Показать версии клиента и фоновой службы" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Показать подробную информацию о фоновой службе для определенного домена" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Показать отладочную информацию для всех файлов" +msgid "Show debugging information for all domains" +msgstr "Показать отладочную информацию для всех доменов" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Показать параметры отладки" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Показать необновляемые устройства" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Показать дополнительную отладочную информацию" +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Показать историю обновлений прошивки" + #. TRANSLATORS: this is for plugin development msgid "Show plugin verbose information" -msgstr "Показать подробную информацию о приложении" +msgstr "Показать подробную информацию о плагине" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Показать журнал отладки с последней попытки обновления" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Показать информацию о состоянии обновления прошивки" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Выключить сейчас?" + +msgid "Sign data using the client certificate" +msgstr "Подписать данные с использованием сертификата клиента" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Подписать данные с использованием сертификата клиента" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Подписать загруженные данные с использованием сертификата клиента" + +msgid "Signature" +msgstr "Подпись" + +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" @@ -548,11 +1050,32 @@ 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: section header for the release one line summary +msgid "Summary" +msgstr "Сводка" + +msgid "Target" +msgstr "Цель" + +#. TRANSLATORS: do not translate the variables marked using $ +msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." +msgstr "LVFS — это бесплатный сервис, действующий как независимое юридическое лицо, которое не имеет связи с системой $OS_RELEASE:NAME$. Ваш распространитель системы может не проверять какие-либо обновления прошивки на совместимость с системой или подключенными устройствами. Каждая прошивка поставляется только производителем оригинального оборудования." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Одобренной прошивки нет." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Эта программа может корректно работать только как root" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "Этот репозиторий содержит прошивки, которое не запрещены, но все еще тестируются производителем оборудования. Вы должны убедиться, что у вас есть способ вручную понизить версию прошивки устройства в случае сбоя при обновлении." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Этот инструмент может использовать только пользователь root" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -566,6 +1089,10 @@ msgid "Type" msgstr "Тип" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Средство для прошивки UEFI" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -579,7 +1106,16 @@ #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" -msgstr "Разблокирует устройство для доступа к микропрограмме" +msgstr "Разблокировать устройство для доступа к прошивке" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Снять флаг отладки во время обновления" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Неподдерживаемая фоновая служба версии %s, клиент версии %s" #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" @@ -589,31 +1125,57 @@ msgid "Update Description" msgstr "Описание обновления" +#. TRANSLATORS: section header for the amount +#. * of time it takes to install the update +msgid "Update Duration" +msgstr "Продолжительность обновления" + #. TRANSLATORS: section header for firmware remote http:// msgid "Update Location" msgstr "Расположение обновления" +#. TRANSLATORS: section header for the release name +msgid "Update Name" +msgstr "Наименование обновления" + #. TRANSLATORS: section header for remote ID, e.g. lvfs-testing msgid "Update Remote ID" -msgstr "Удалённый ID обновления" +msgstr "Обновить идентификатор репозитория" + +#. TRANSLATORS: section header for the release one line summary +msgid "Update Summary" +msgstr "Сводка обновления" #. TRANSLATORS: section header for firmware version 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:" +msgstr "Ошибка обновления — известная проблема, посетите этот URL для получения дополнительной информации:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Обновить сейчас?" msgid "Update the stored device verification information" -msgstr "Обновление хранимой проверочной информации устройства" +msgstr "Обновить хранимую проверочную информацию устройства" #. TRANSLATORS: command description msgid "Update the stored metadata with current ROM contents" -msgstr "Обновить хранимые метаданные с текущим содержимым ПЗУ" +msgstr "Обновить сохранённые метаданные с текущим содержимым ПЗУ" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Обновить сохраненные метаданные с текущим содержимым" #. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" -msgstr "Обновляет все микропрограммы до их последних доступных версий" +msgstr "Обновить все прошивки до их последних доступных версий" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are @@ -622,6 +1184,23 @@ 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 "Загрузить сообщение:" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Загрузить отчет сейчас?" + +#. TRANSLATORS: explain why we want to upload +msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +msgstr "Загрузка отчетов о прошивке помогает поставщикам оборудования быстро определять неудачные и успешные обновления на реальных устройствах." + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Имя пользователя" @@ -640,19 +1219,24 @@ #. TRANSLATORS: command description msgid "Watch DFU devices being hotplugged" -msgstr "Наблюдать за устройствами DFU, которые являются подключёнными" +msgstr "Следить за устройствами DFU, которые являются подключёнными" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Следить за аппаратными изменениями" #. TRANSLATORS: command description msgid "Write firmware from file into device" -msgstr "Записать микропрограмму из файла на устройство" +msgstr "Записать прошивку из файла на устройство" #. TRANSLATORS: command description msgid "Write firmware from file into one partition" -msgstr "Записать микропрограмму из файла на один раздел" +msgstr "Записать прошивку из файла на один раздел" #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Запись…" -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 "Ваш распространитель системы может не проверять какие-либо обновления прошивки на совместимость с системой или подключенными устройствами." diff -Nru fwupd-1.0.9/po/sk.po fwupd-1.2.10/po/sk.po --- fwupd-1.0.9/po/sk.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/sk.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: Slovak (http://www.transifex.com/freedesktop/fwupd/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -126,6 +123,7 @@ msgid "Device removed:" msgstr "Odstránené zariadenie:" +#. success msgid "Done!" msgstr "Hotovo!" @@ -206,10 +204,6 @@ msgid "Install old version of system firmware" msgstr "Nainštaluje staršiu verziu firmvéru systému" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Nainštaluje pripravené aktualizácie ihneď" - msgid "Install signed device firmware" msgstr "Nainštaluje podpísaný firmvér zariadenia" @@ -315,10 +309,6 @@ msgid "Sets metadata on a firmware file" msgstr "Nastaví metaúdaje pre súbor s firmvérom" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Zobrazí ladiace informácie pre všetky súbory" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Zobrazí voľby ladenia" diff -Nru fwupd-1.0.9/po/sr.po fwupd-1.2.10/po/sr.po --- fwupd-1.0.9/po/sr.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/sr.po 2019-07-15 18:25:54.000000000 +0000 @@ -10,9 +10,6 @@ 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: Serbian (http://www.transifex.com/freedesktop/fwupd/language/sr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -46,7 +43,7 @@ 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 "Потребно је поново покретање да би се исправка применила." @@ -212,6 +209,7 @@ msgid "Do not check for unreported history" msgstr "Не проверавај непослати историјат" +#. success msgid "Done!" msgstr "Урађено!" @@ -372,10 +370,6 @@ msgid "Install old version of system firmware" msgstr "Инсталирајте старо издање системског фирмвера" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Инсталирај припремљена ажурирања сад" - msgid "Install signed device firmware" msgstr "Инсталирајте потписани фирмвер за уређаје" @@ -595,10 +589,6 @@ msgid "Show client and daemon versions" msgstr "Прикажи издања клијента и демона" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Прикажи податке о отклањању проблема за све датотеке" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Прикажи опције за отклањање проблема" @@ -632,12 +622,6 @@ 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 "Наслов" @@ -693,9 +677,6 @@ 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 "Узрок неуспеха ажурирања је познат, погледајте ову адресу за више података:" @@ -761,6 +742,3 @@ #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Пишем…" - -msgid "fwupd" -msgstr "fwupd" diff -Nru fwupd-1.0.9/po/sv.po fwupd-1.2.10/po/sv.po --- fwupd-1.0.9/po/sv.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/sv.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,18 +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" -"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: Swedish (http://www.transifex.com/freedesktop/fwupd/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -22,11 +19,46 @@ "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" @@ -52,7 +84,7 @@ 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." @@ -64,10 +96,18 @@ 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" @@ -190,6 +230,10 @@ 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:" @@ -210,11 +254,18 @@ 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" @@ -226,6 +277,11 @@ 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" + +#. success msgid "Done!" msgstr "Klar!" @@ -240,6 +296,11 @@ 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..." @@ -256,6 +317,14 @@ 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?" @@ -264,10 +333,16 @@ 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." @@ -292,6 +367,10 @@ 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" @@ -347,6 +426,15 @@ 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" @@ -355,10 +443,18 @@ 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" @@ -391,16 +487,16 @@ 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" msgid "Install old version of system firmware" msgstr "Installera en gammal version av fast programvara för systemet" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Installera förberedda uppdateringar nu" - msgid "Install signed device firmware" msgstr "Installera signerad fast programvara för enhet" @@ -417,9 +513,18 @@ 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)" @@ -430,10 +535,18 @@ 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" @@ -470,11 +583,18 @@ 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." @@ -484,7 +604,11 @@ #. 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" @@ -501,6 +625,12 @@ 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" @@ -512,6 +642,10 @@ 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" @@ -619,6 +753,10 @@ 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" @@ -639,26 +777,40 @@ msgid "Show client and daemon versions" msgstr "Visa klient- och demon-version" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Visa felsökningsinformation för alla filer" - #. TRANSLATORS: for the --verbose arg 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" @@ -676,11 +828,16 @@ 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." + +#. 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 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." +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" @@ -694,6 +851,10 @@ 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" @@ -709,6 +870,10 @@ 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" @@ -717,6 +882,11 @@ 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" @@ -737,9 +907,6 @@ 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:" @@ -766,6 +933,11 @@ 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:" @@ -799,6 +971,10 @@ 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" @@ -813,6 +989,3 @@ #. 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 -Nru fwupd-1.0.9/po/test-deps fwupd-1.2.10/po/test-deps --- fwupd-1.0.9/po/test-deps 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/test-deps 2019-07-15 18:25:54.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/python3 """ Check dependencies needed for rasterization """ """ diff -Nru fwupd-1.0.9/po/tr.po fwupd-1.2.10/po/tr.po --- fwupd-1.0.9/po/tr.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/tr.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: Turkish (http://www.transifex.com/freedesktop/fwupd/language/tr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -83,6 +80,7 @@ msgid "Device removed:" msgstr "Aygıt çıkarıldı:" +#. success msgid "Done!" msgstr "Bitti!" diff -Nru fwupd-1.0.9/po/uk.po fwupd-1.2.10/po/uk.po --- fwupd-1.0.9/po/uk.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/uk.po 2019-07-15 18:25:54.000000000 +0000 @@ -3,14 +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" -"POT-Creation-Date: 2018-06-06 14:54+0100\n" -"PO-Revision-Date: 2018-06-06 14:31+0000\n" -"Last-Translator: Yuri Chornoivan \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" @@ -18,11 +15,126 @@ "Language: uk\n" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Лишилася %.0f хвилина" +msgstr[1] "Лишилося %.0f хвилини" +msgstr[2] "Лишилося %.0f хвилин" +msgstr[3] "Лишилася %.0f хвилина" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Оновлення %s Consumer ME" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Оновлення для контролера %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "Оновлення %s Corporate ME" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Оновлення для пристрою %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Оновлення для вбудованого контролера %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Оновлення ME %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Оновлення системи %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Оновлення для %s" + #. TRANSLATORS: first replacement is device name #, c-format 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: command description +msgid "Activate devices" +msgstr "Задіяти пристрої" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Задіяти пристрої у черзі очікування" + +msgid "Activate the new firmware on the device" +msgstr "Активація нової мікропрограми на пристрої" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Активуємо оновлення мікропрограми" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Активуємо оновлення мікропрограми для" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Додано" @@ -48,10 +160,14 @@ 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 "Відповідати «так» на усі питання" @@ -60,6 +176,19 @@ msgid "Apply a binary patch" msgstr "Застосувати бінарну латку" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Застосувати оновлення мікропрограми" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Схвалена мікропрограма:" +msgstr[1] "Схвалені мікропрограми:" +msgstr[2] "Схвалені мікропрограми:" +msgstr[3] "Схвалена мікропрограма:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Повернути пристрій із можливостями DFU до використання" @@ -90,6 +219,22 @@ msgstr "Для внесення змін до записів налаштованих віддалених пристроїв, які використовуються для оновлення мікропрограм, слід пройти розпізнавання" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Для внесення змін до налаштувань фонової служби слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Щоб встановити список схвалених мікропрограм, вам слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Щоб підписати дані за допомогою клієнтського сертифіката, вам слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Щоб перемкнутися на нову версію мікропрограми, вам слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Щоб розблокувати пристрій, слід пройти розпізнавання" @@ -194,6 +339,10 @@ msgid "Detach to bootloader mode" msgstr "Від'єднатися до режиму завантажувача" +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Ід. пристрою" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Додано пристрій:" @@ -214,11 +363,18 @@ msgid "Devices that were not updated correctly:" msgstr "Пристрої, для яких не вдалося оновити дані належним чином:" +msgid "Disabled fwupdate debugging" +msgstr "Вимкнено діагностику fwupdate" + #. TRANSLATORS: command description msgid "Disables a given remote" msgstr "Вимикає вказане віддалене сховище" #. TRANSLATORS: command line option +msgid "Display version" +msgstr "Показати версію" + +#. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Не перевіряти, чи є застарілі метадані" @@ -234,6 +390,7 @@ msgid "Do not write to the history database" msgstr "Не записувати дані до бази даних журналу" +#. success msgid "Done!" msgstr "Виконано!" @@ -248,6 +405,11 @@ 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 "Отримуємо дані…" @@ -264,6 +426,14 @@ msgid "Dump information about a binary patch to the screen" msgstr "Вивести дамп інформації щодо бінарної латки на екран" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Вказаний ESP є некоректним" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Увімкнути підтримку оновлення мікропрограми на підтримуваних системах" + #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" msgstr "Увімкнути це віддалене сховище?" @@ -272,10 +442,16 @@ msgid "Enabled" msgstr "Увімкнено" +msgid "Enabled fwupdate debugging" +msgstr "Увімкнено діагностику fwupdate" + #. TRANSLATORS: command description msgid "Enables a given remote" msgstr "Вмикає вказане віддалене сховище" +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "Наслідки вмикання цієї можливості покладаються на вас. Це означає, що усі проблеми, пов'язані із цими оновленнями, ви маєте вирішувати із виробником обладнання. Повідомляти розробникам дистрибутива за адресою $OS_RELEASE:BUG_REPORT_URL$ слід лише про помилки у процесі оновлення." + #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." msgstr "Вмикання цього віддаленого сховища виконано під вашу відповідальність." @@ -300,10 +476,22 @@ msgid "Exit after the engine has loaded" msgstr "Завершити роботу після завантаження рушія" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Не вдалося встановити з'єднання із фоновою службою" + #. TRANSLATORS: the server is rate-limiting downloads msgid "Failed to download due to server limit" msgstr "Не вдалося отримати через обмеження сервера" +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Не вдалося отримати список пристроїв у черзі очікування" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Не вдалося встановити оновлення мікропрограми" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Не вдалося завантажити коригування" @@ -312,6 +500,14 @@ msgid "Failed to parse arguments" msgstr "Не вдалося обробити аргументи" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Не вдалося перезавантажити" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Не вдалося встановити режим вітання" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Отримуємо файл" @@ -336,6 +532,10 @@ msgid "Filename Signature" msgstr "Підпис назви файла" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Агент мікропрограм" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "Основна адреса мікропрограми" @@ -361,6 +561,19 @@ msgstr[2] "Метадані мікропрограми не оновлювалися %u днів, можливо, вони вже не є актуальними." msgstr[3] "Метадані мікропрограми не оновлювалися %u днів, можливо, вони вже не є актуальними." +msgid "Firmware updates are not supported on this machine." +msgstr "На цьому комп'ютері не передбачено підтримки оновлення мікропрограми." + +msgid "Firmware updates are supported on this machine." +msgstr "На цьому комп'ютері передбачено підтримку оновлень мікропрограми." + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "Прапорці" + +msgid "Force the action ignoring all warnings" +msgstr "Виконати дію примусово, ігноруючи усі попередження" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "Знайдено" @@ -373,6 +586,10 @@ msgstr "Отримати список усіх пристроїв за топологією системи" #. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Отримати список усіх пристроїв та можливих випусків" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Отримати список усіх пристроїв, у яких передбачено оновлення мікропрограми" @@ -392,6 +609,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Отримує криптографічні хеш-суми для дампів мікропрограм" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Отримує список схвалених мікропрограм." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Отримує список оновлень для з’єднаного обладнання" @@ -423,10 +644,6 @@ msgid "Install old version of system firmware" msgstr "Встановити стару версію мікропрограми системи" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "Встановити приготовані оновлення зараз" - msgid "Install signed device firmware" msgstr "Встановити підписану мікропрограму пристрою" @@ -439,13 +656,26 @@ msgid "Install unsigned system firmware" msgstr "Встановити непідписану мікропрограму системи" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Встановлюємо мікропрограму…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Встановлюємо оновлення мікропрограми…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Встановлюємо на %s…" + msgid "Keyring" msgstr "Сховище ключів" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Лишилося менше за хвилину" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Служба надання мікропрограм для Linux від виробника (стабільна мікропрограма)" @@ -456,6 +686,10 @@ msgid "List currently attached DFU capable devices" msgstr "Вивести поточний список долучених пристроїв із можливостями DFU" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Показати список підтримуваних оновлень мікропрограми" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Завантаження…" @@ -480,9 +714,18 @@ msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Метадані можна отримати від служби надання мікропрограм для Linux від виробника." +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Невідповідність фонової служби і клієнта, скористайтеся краще %s" + msgid "Mode" msgstr "Режим" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Змінює значення налаштування фонової служби." + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Змінює вказаний запис віддаленого пристрою" @@ -490,6 +733,9 @@ msgid "Modify a configured remote" msgstr "Зміна налаштованих віддалених пристроїв" +msgid "Modify daemon configuration" +msgstr "Зміна налаштувань фонової служби" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Стежити за подіями у фоновій службі" @@ -500,6 +746,9 @@ msgid "Name" msgstr "Назва" +msgid "No action specified!" +msgstr "Не вказано дії!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" @@ -513,6 +762,10 @@ msgid "No remotes are currently enabled so no metadata is available." msgstr "Зараз не увімкнено жодного віддаленого сховища, отже, метадані недоступні." +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Не застосовано жодного оновлення" + msgid "OK" msgstr "Гаразд" @@ -520,6 +773,14 @@ msgid "Override plugin warning" msgstr "Перевизначити попередження для додатка" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Перевизначити типовий шлях до ESP" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Не зважати на попередження і примусово виконати дію" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Пароль" @@ -527,6 +788,10 @@ msgid "Payload" msgstr "Вміст" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Відсоток виконання" + msgid "Permission denied" msgstr "Доступ заборонено" @@ -535,6 +800,12 @@ 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 "Пріоритетність" @@ -546,6 +817,10 @@ msgid "Protocol" msgstr "Протокол" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Опитати щодо підтримки оновлення мікропрограми" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -563,6 +838,10 @@ msgid "Reading…" msgstr "Читаємо…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Перезавантажуємо…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Оновити метадані з віддаленого сервера" @@ -610,6 +889,10 @@ msgid "Restart now?" msgstr "Перезавантажити зараз?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Перезапустити фонову службу, щоб зміни набули чинності?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Перезапускаємо пристрій…" @@ -618,10 +901,22 @@ msgid "Return all the hardware IDs for the machine" msgstr "Повернути усі ідентифікатори апаратного забезпечення комп’ютера" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Запустити процедуру чищення композиції додатків при використанні install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Запустити процедуру приготування композиції додатків при використанні install-blob" + msgid "Runtime" msgstr "Час обробляння" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Зберігати стан пристрою до файла JSON між виконаннями" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Якщо можливо, запланувати встановлення на наступне перезавантаження" @@ -653,6 +948,10 @@ msgid "Set release version on firmware file" msgstr "Встановити версію випуску для файла мікропрограми" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Встановити під час оновлення прапорець діагностики" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "Встановити розмір мікропрограми для призначення" @@ -665,6 +964,13 @@ msgid "Sets metadata on a firmware file" msgstr "Встановлює метадані щодо файла мікпропрограми" +msgid "Sets the list of approved firmware" +msgstr "Встановлення списку схвалених мікропрограм" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Встановлює список схвалених мікропрограм." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Поділитися журналом оновлень із розробниками" @@ -673,9 +979,13 @@ msgid "Show client and daemon versions" msgstr "Вивести дані щодо версій клієнат і фонової служби" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Показати докладні дані фонової служби для певного домену" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "Показувати діагностичні дані для всіх файлів" +msgid "Show debugging information for all domains" +msgstr "Показати діагностичні дані для усіх доменів" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" @@ -697,6 +1007,38 @@ msgid "Show plugin verbose information" msgstr "Показати докладні відомості щодо додатків" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Показати діагностичний журнал щодо останньої спроби оновлення" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Показати дані щодо стану оновлення мікропрограми" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Вимкнути зараз?" + +msgid "Sign data using the client certificate" +msgstr "Підписування даних клієнтським сертифікатом" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Підписування даних клієнтським сертифікатом" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Підписати вивантажені дані клієнтським сертифікатом" + +msgid "Signature" +msgstr "Підпис" + +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 "Стан" @@ -714,15 +1056,25 @@ 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 є простою фоновою службою, яка надає змогу оновлювати мікропрограми пристроїв на вашому локальному комп’ютері у межах сеансу користування. Програму розроблено для робочих станцій, але нею можна скористатися на телефонах, планшетах та серверах без дисплеїв." +#. TRANSLATORS: do not translate the variables marked using $ +msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." +msgstr "LVFS є безкоштовною службою, яка працює як незалежна юридична одиниця і не має зв'язку з $OS_RELEASE:NAME$. Розробники вашої операційної системи, можливо, не перевіряли жодні з цих оновлень на сумісність із системою або з'єднаними із комп'ютером пристроями. Усі мікропрограми надаються лише самими виробниками обладнання." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +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 "У цьому сховищі міститься мікропрограма, встановлювати яку не заборонено, але яка усе ще перебуває у процесі тестування виробником обладнання. Вам слід переконатися, що ви зможете встановити стабільну версію мікропрограми, якщо процедура оновлення зазнає невдачі." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Цим інструментом може користуватися лише root" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -736,6 +1088,10 @@ msgid "Type" msgstr "Тип" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Засіб роботи із мікропрограмами UEFI" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "Адреса" @@ -751,6 +1107,15 @@ msgid "Unlocks the device for firmware access" msgstr "Розблоковує пристрій для доступу до мікропрограми" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Зняти під час оновлення прапорець діагностики" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Непідтримувана версія фонової служби %s, версія клієнта — %s" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "Контрольна сума оновлення" @@ -759,6 +1124,11 @@ 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 "Місце оновлення" @@ -779,8 +1149,9 @@ 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:" @@ -798,6 +1169,10 @@ msgstr "Оновити збережені метадані на основі поточного вмісту ROM" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Оновити збережені метадані поточними даними" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Оновлює усі мікропрограми до найновіших доступних версій" @@ -808,6 +1183,11 @@ 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 "Повідомлення про вивантаження:" @@ -859,6 +1239,3 @@ #. 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 -Nru fwupd-1.0.9/po/zh_CN.po fwupd-1.2.10/po/zh_CN.po --- fwupd-1.0.9/po/zh_CN.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/zh_CN.po 2019-07-15 18:25:54.000000000 +0000 @@ -4,7 +4,7 @@ # # Translators: # Boyuan Yang <073plan@gmail.com>, 2017 -# Dz Chen , 2016-2017 +# Dingzhong Chen , 2016-2019 # Mingcong Bai , 2017-2018 # Mingye Wang , 2016 # Mingye Wang , 2015 @@ -12,9 +12,6 @@ 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: 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" @@ -22,11 +19,111 @@ "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "还剩 %.0f 分钟" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s 消费者 ME 更新" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s 控制器更新" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s 企业 ME 更新" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s 设备更新" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s 嵌入式控制器更新" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s 管理引擎(ME)更新" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s 系统更新" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s 更新" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" msgstr "%s 有固件更新:" +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u 天" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u 小时" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u 分钟" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u 秒" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "激活设备" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "激活待处理的设备" + +msgid "Activate the new firmware on the device" +msgstr "激活设备上的新固件" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "正在激活固件更新" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "正在激活固件更新给" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "已添加" @@ -52,10 +149,14 @@ 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 "一律用“是”回答问题" @@ -64,10 +165,24 @@ msgid "Apply a binary patch" msgstr "应用二进制补丁" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "应用固件更新" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "批准的固件:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "将可固件升级 (DFU) 的设备重新附到运行时上" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "附加到固件模式" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -90,6 +205,22 @@ msgstr "需要认证:修改用于固件更新的已配置远程位置" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "需要认证:修改守护进程配置" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "需要认证:设定批准固件的列表" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "需要认证:使用客户端证书签名数据" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "需要认证:切换到新固件版本" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "需要认证:解锁设备" @@ -133,7 +264,7 @@ #. TRANSLATORS: get interactive prompt msgid "Choose a device:" -msgstr "选择一个设备:" +msgstr "选择设备:" #. TRANSLATORS: get interactive prompt msgid "Choose a release:" @@ -190,6 +321,14 @@ msgid "Detach currently attached DFU capable device" msgstr "断开当前连接的可固件升级的设备" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "拆离到引导加载器模式" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "设备 ID" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "已添加设备:" @@ -210,11 +349,18 @@ msgid "Devices that were not updated correctly:" msgstr "未正确更新的设备:" +msgid "Disabled fwupdate debugging" +msgstr "禁用 fwupdate 调试" + #. TRANSLATORS: command description msgid "Disables a given remote" msgstr "禁用指定远程功能" #. TRANSLATORS: command line option +msgid "Display version" +msgstr "显示版本" + +#. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "不要查找老的元数据" @@ -226,6 +372,11 @@ msgid "Do not check for unreported history" msgstr "不要查找未报告历史" +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "不要写入历史数据库" + +#. success msgid "Done!" msgstr "完成!" @@ -240,6 +391,11 @@ 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 "正在下载..." @@ -256,6 +412,14 @@ msgid "Dump information about a binary patch to the screen" msgstr "将二进制补丁信息输出到屏幕" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "指定的 ESP 无效" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "在所支持的系统上启用固件更新的支持" + #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" msgstr "要启用此远程功能吗?" @@ -264,10 +428,16 @@ msgid "Enabled" msgstr "已启用" +msgid "Enabled fwupdate debugging" +msgstr "启用 fwupdate 调试" + #. TRANSLATORS: command description msgid "Enables a given remote" msgstr "启用指定远程功能" +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "启用此功能的风险自负,这意味着你必须就这些更新引起的任何问题与原始设备制造商联系。只有更新过程本身的问题可以提交到 $OS_RELEASE:BUG_REPORT_URL$。" + #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." msgstr "启用此远程功能后果自负。" @@ -292,6 +462,22 @@ msgid "Exit after the engine has loaded" msgstr "在引擎加载后退出" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "连接到守护进程失败" + +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "由于服务器限制而下载失败" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "获取待处理的设备失败" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "安装固件更新失败" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "载入特定问题失败" @@ -300,6 +486,14 @@ msgid "Failed to parse arguments" msgstr "未能解析参数" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "重启失败" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "设定启动屏幕模式失败" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "正在获取文件" @@ -324,6 +518,10 @@ msgid "Filename Signature" msgstr "文件名签名" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "固件代理" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "固件库 URI" @@ -346,6 +544,19 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." msgstr[0] "固件元数据已有 %u 天未更新,数据可能不是最新的。" +msgid "Firmware updates are not supported on this machine." +msgstr "此机器不支持固件更新。" + +msgid "Firmware updates are supported on this machine." +msgstr "此机器支持固件更新。" + +#. TRANSLATORS: section header for firmware flags +msgid "Flags" +msgstr "标志" + +msgid "Force the action ignoring all warnings" +msgstr "强制操作忽略所有警告" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "找到" @@ -354,10 +565,22 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "根据系统拓扑获取所有设备" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "获取所有设备和可用版本" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "获得所有支持更新固件的硬件列表" #. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "获取所有在系统注册的启用插件" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "获取有关某固件文件的详细信息" @@ -369,6 +592,10 @@ msgid "Gets the cryptographic hash of the dumped firmware" msgstr "获取转储出的固件的校验和" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "获取批准固件的列表。" + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "获取已连接硬件的可用更新列表" @@ -390,16 +617,16 @@ msgstr "空闲…" #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "在设备上安装固件比特块" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" -msgstr "安装此硬件上的固件文件" +msgstr "在此硬件上安装固件文件" msgid "Install old version of system firmware" msgstr "安装旧版本的系统固件" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "现在安装准备好的更新" - msgid "Install signed device firmware" msgstr "安装已签名的设备固件" @@ -412,13 +639,26 @@ msgid "Install unsigned system firmware" msgstr "安装未签名的系统固件" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "正在安装固件……" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "正在安装固件更新..." +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "正在安装到 %s……" + msgid "Keyring" msgstr "密钥环" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "还剩不到一分钟" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux 供应商固件服务(稳定固件)" @@ -429,10 +669,18 @@ msgid "List currently attached DFU capable devices" msgstr "列出当前连接的可固件升级的设备" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "列出所支持固件的更新" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "正在加载…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "将指定插件手动加入白名单" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "将多个固件文件合并为一个" @@ -449,9 +697,18 @@ msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "可从 Linux 供应商固件服务获取元数据。" +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "守护进程与客户端不匹配,使用 %s 来代替" + msgid "Mode" msgstr "模式" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "修改守护进程配置值。" + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "修改给定的远程位置" @@ -459,6 +716,9 @@ msgid "Modify a configured remote" msgstr "修改已配置的远程位置" +msgid "Modify daemon configuration" +msgstr "修改守护进程配置" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "监视守护程序里的事件" @@ -469,15 +729,26 @@ msgid "Name" msgstr "名称" +msgid "No action specified!" +msgstr "未指定操作!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "没有检测到支持更新固件的硬件" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "未找到插件" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "当前尚未启用任何远程功能,因此无可用元数据。" +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "没有应用任何更新" + msgid "OK" msgstr "确定" @@ -485,6 +756,14 @@ msgid "Override plugin warning" msgstr "忽略插件警告" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "覆盖默认的 ESP 路径" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "覆盖警告并强制执行操作" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "密码" @@ -492,6 +771,10 @@ msgid "Payload" msgstr "载荷" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "完成百分比" + msgid "Permission denied" msgstr "拒绝授权" @@ -500,6 +783,12 @@ 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 "优先级" @@ -511,6 +800,10 @@ msgid "Protocol" msgstr "协议" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "查询固件更新的支持" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -528,6 +821,10 @@ msgid "Reading…" msgstr "正在读取…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "正在重启……" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "刷新来自远程服务器的元数据" @@ -575,6 +872,10 @@ msgid "Restart now?" msgstr "要现在重启设备吗?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "重启守护进程以让更改生效?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "正在重启设备…" @@ -583,10 +884,22 @@ msgid "Return all the hardware IDs for the machine" msgstr "返回机器的所有硬件 ID" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "当使用安装比特块时运行插件组合清理例程" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "当使用安装比特块时运行插件组合准备例程" + msgid "Runtime" msgstr "运行时" #. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "在执行之间保存设备状态到 JSON 文件" + +#. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "如有可能,安排安装到下次重启" @@ -618,6 +931,10 @@ msgid "Set release version on firmware file" msgstr "设置固件文件上的发布版本" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "更新期间设定调试标志" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "为目标固件设置大小" @@ -630,6 +947,13 @@ msgid "Sets metadata on a firmware file" msgstr "设置固件文件上的元数据" +msgid "Sets the list of approved firmware" +msgstr "设定批准固件的列表" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "设定批准固件的列表。" + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "与开发者分享固件历史" @@ -638,15 +962,23 @@ msgid "Show client and daemon versions" msgstr "显示客户端及守护程序版本" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "显示特定域的守护进程详细信息" + #. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "显示所有文件的调试信息" +msgid "Show debugging information for all domains" +msgstr "显示所有域的调试信息" #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "显示调试选项" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "显示不可更新的设备" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "显示额外调试信息" @@ -658,6 +990,38 @@ msgid "Show plugin verbose information" msgstr "显示插件详细回显信息" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "显示上次尝试更新的调试日志" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "显示固件更新状态的信息" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "现在关机?" + +msgid "Sign data using the client certificate" +msgstr "使用客户端证书签名数据" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "使用客户端证书签名数据" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "以客户端证书签名上传的数据" + +msgid "Signature" +msgstr "签名" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "指定 DFU 设备的供应商/产品 ID" + +msgid "Specify the number of bytes per USB transfer" +msgstr "指定每次 USB 传输的字节数" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "状态" @@ -675,11 +1039,25 @@ 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: do not translate the variables marked using $ +msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." +msgstr "LVFS 是个免费服务,以独立法律实体运作,与 $OS_RELEASE:NAME$ 无关。你的发行商可能未对固件更新与你的系统或连接的设备兼容性做过任何验证。所有固件都由原始设备制造商提供。" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "没有批准的固件。" + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "此程序只能以 root 身份正常运行" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "此远程源包含未禁止的固件,但其仍经过硬件供应商测试。你应该确保如果固件更新失败时你能够降级固件。" + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "此工具只能由 root 用户使用" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -693,6 +1071,10 @@ msgid "Type" msgstr "类型" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "固件 UEFI 实用工具" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -708,6 +1090,15 @@ msgid "Unlocks the device for firmware access" msgstr "为固件访问解锁设备" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "更新期间取消设定调试标志" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "未受支持的守护进程版本 %s,客户端版本为 %s" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "更新校验和" @@ -716,6 +1107,11 @@ 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 "更新位置" @@ -736,8 +1132,9 @@ 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:" @@ -755,6 +1152,10 @@ msgstr "使用目前的 ROM 内容更新存储的元数据" #. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "使用当前内容更新存储的元数据" + +#. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "将所有固件都更新为最新版本" @@ -765,6 +1166,11 @@ 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 "上传消息:" @@ -798,6 +1204,10 @@ msgstr "注意被热插入的固件升级 (DFU) 设备" #. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "监视硬件更改" + +#. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "将来自文件的固件写入设备" @@ -812,6 +1222,3 @@ #. 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 -Nru fwupd-1.0.9/po/zh_TW.po fwupd-1.2.10/po/zh_TW.po --- fwupd-1.0.9/po/zh_TW.po 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/po/zh_TW.po 2019-07-15 18:25:54.000000000 +0000 @@ -8,9 +8,6 @@ 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: 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" @@ -18,6 +15,12 @@ "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "剩下 %.0f 分鐘" + #. TRANSLATORS: first replacement is device name #, c-format msgid "%s has firmware updates:" @@ -31,6 +34,10 @@ msgid "Age" msgstr "年紀" +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "同意並且啟用遠端站點?" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" @@ -44,7 +51,7 @@ 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 "有更新必須重新開機才能完成。" @@ -56,10 +63,18 @@ msgid "Apply a binary patch" msgstr "套用二進位補丁" +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "套用韌體更新" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "將 DFU 能力裝置接回執行時期" +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "連接至韌體模式" + #. TRANSLATORS: device attributes, i.e. things that #. * the device can do msgid "Attributes" @@ -79,7 +94,7 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" -msgstr "必須通過身份核對才能修改設定作韌體更新的遠端" +msgstr "必須通過身份核對才能修改設定作韌體更新的遠端站點" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" @@ -182,6 +197,10 @@ msgid "Detach currently attached DFU capable device" msgstr "解開目前接上的 DFU 能力裝置" +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "斷開至開機載入器模式" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "裝置已加入:" @@ -202,6 +221,17 @@ msgid "Devices that were not updated correctly:" msgstr "無法正確更新的裝置:" +msgid "Disabled fwupdate debugging" +msgstr "已停用 fwupdate 除錯" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "停用指定的遠端站點" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "顯示版本" + #. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "不要檢查中介資料是否老舊" @@ -214,6 +244,11 @@ msgid "Do not check for unreported history" msgstr "不要檢查是否有尚未報告的歷史" +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "不要寫入歷史資料庫中" + +#. success msgid "Done!" msgstr "完成!" @@ -226,7 +261,7 @@ #. * version numbers e.g. "1.2.3" #, c-format msgid "Downgrading %s from %s to %s... " -msgstr "正降級 %s 從 %s 版至 %s 版…" +msgstr "正將 %s 從 %s 版降級至 %s 版... " #. TRANSLATORS: downloading from a remote server msgid "Downloading…" @@ -244,10 +279,36 @@ msgid "Dump information about a binary patch to the screen" msgstr "傾印二進位補丁相關資訊至螢幕上" +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "指定的 ESP 無效" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "對支援的系統啟用韌體更新支援" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "啟用此遠端站點?" + #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "啟用" +msgid "Enabled fwupdate debugging" +msgstr "已啟用 fwupdate 除錯" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "啟用指定的遠端站點" + +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "啟用此功能必須自負風險。這代表若您遭遇到這些更新導致的任何問題,您必須向原始設備供應商聯絡並反應。只有在您遇到更新過程本身的問題時,才是向 $OS_RELEASE:BUG_REPORT_URL$ 回報。" + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "若要啟用此遠端站點,請自負風險。" + #. TRANSLATORS: command description msgid "Encrypt firmware data" msgstr "加密韌體資料" @@ -268,6 +329,10 @@ msgid "Exit after the engine has loaded" msgstr "引擎載入後離開" +#. TRANSLATORS: the server is rate-limiting downloads +msgid "Failed to download due to server limit" +msgstr "因伺服器限制而下載失敗" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "無法載入奇技淫巧" @@ -322,6 +387,12 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." msgstr[0] "韌體中介資料已有 %u 天未更新,可能不是最新狀態。" +msgid "Firmware updates are not supported on this machine." +msgstr "此機器沒有韌體更新支援。" + +msgid "Firmware updates are supported on this machine." +msgstr "此機器有韌體更新支援。" + #. TRANSLATORS: detected a DFU device msgid "Found" msgstr "找到" @@ -330,16 +401,24 @@ msgstr "GUID" #. TRANSLATORS: command description +msgid "Get all devices according to the system topology" +msgstr "取得根據系統拓樸而得的所有裝置" + +#. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "取得所有支援韌體更新的裝置" #. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "取得所有系統中註冊的啟用插件" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "取得韌體檔案的相關細節" #. TRANSLATORS: command description msgid "Gets the configured remotes" -msgstr "取得設定的遠端" +msgstr "取得設定的遠端站點" #. TRANSLATORS: command description msgid "Gets the cryptographic hash of the dumped firmware" @@ -366,16 +445,16 @@ msgstr "閒置…" #. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "在裝置上安裝韌體 blob" + +#. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "在此硬體安裝韌體檔案" msgid "Install old version of system firmware" msgstr "安裝舊版本的系統韌體" -#. TRANSLATORS: command description -msgid "Install prepared updates now" -msgstr "立刻安裝準備的更新" - msgid "Install signed device firmware" msgstr "安裝已簽署的裝置韌體" @@ -392,17 +471,40 @@ msgid "Installing firmware update…" msgstr "安裝韌體更新中…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "正安裝到 %s…" + msgid "Keyring" msgstr "鑰匙圈" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "剩不到一分鐘" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux 廠商韌體服務(穩定版韌體)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux 廠商韌體服務(測試中韌體)" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "列出目前接上的 DFU 能力裝置" +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "列出支援的韌體更新" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "載入中…" +#. TRANSLATORS: command line option +msgid "Manually whitelist specific plugins" +msgstr "手動白名單指定插件" + #. TRANSLATORS: command description msgid "Merge multiple firmware files into one" msgstr "合併多份韌體檔案為一份" @@ -415,15 +517,19 @@ msgid "Metadata URI Signature" msgstr "中介資料 URI 簽章" +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "中介資料可從 Linux 廠商韌體服務取得。" + msgid "Mode" msgstr "模式" #. TRANSLATORS: command description msgid "Modifies a given remote" -msgstr "修改指定的遠端" +msgstr "修改指定的遠端站點" msgid "Modify a configured remote" -msgstr "修改設定的遠端" +msgstr "修改設定的遠端站點" #. TRANSLATORS: command description msgid "Monitor the daemon for events" @@ -435,11 +541,22 @@ msgid "Name" msgstr "名稱" +msgid "No action specified!" +msgstr "未指定動作!" + #. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "未偵測到具備韌體更新能力的硬體" +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "找不到插件" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "目前沒有啟用的遠端站點,因而沒有中介資料可用。" + msgid "OK" msgstr "確定" @@ -447,6 +564,10 @@ msgid "Override plugin warning" msgstr "凌駕插件警告" +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "凌駕預設 ESP 路徑" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "密碼" @@ -473,6 +594,10 @@ msgid "Protocol" msgstr "協定" +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "查詢韌體更新支援" + #. TRANSLATORS: device quirks, i.e. things that #. * it does that we have to work around msgid "Quirks" @@ -503,7 +628,7 @@ #. * e.g. "1.2.3" #, c-format msgid "Reinstalling %s with %s... " -msgstr "正重新安裝 %s %s 版…" +msgstr "正將 %s 重新安裝為 %s 版... " #. TRANSLATORS: section header for the remote the file is coming from msgid "Remote" @@ -580,6 +705,10 @@ msgid "Set release version on firmware file" msgstr "設定韌體檔案發行版本" +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "在更新期間設定除錯旗標" + #. TRANSLATORS: command description msgid "Set the firmware size for the target" msgstr "設定目標的韌體檔案大小" @@ -600,15 +729,15 @@ msgid "Show client and daemon versions" msgstr "顯示客戶端與幕後程式版本" -#. TRANSLATORS: turn on all debugging -msgid "Show debugging information for all files" -msgstr "顯示所有檔案的除錯資訊" - #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "顯示除錯選項" #. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "顯示不可更新的裝置" + +#. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "顯示額外除錯資訊" @@ -620,6 +749,14 @@ msgid "Show plugin verbose information" msgstr "顯示插件詳盡資訊" +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "顯示從上次試圖更新起的除錯紀錄" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "顯示韌體更新狀態的資訊" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "狀態" @@ -637,11 +774,16 @@ 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 程序是個簡易幕後程式,允許工作階段軟體更新您本地端機器上的裝置韌體。它主要設計給桌面電腦使用,但本專案也能用於手機、平板,或是指令列介面伺服器。" +#. 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(Linux 廠商韌體服務,Linux Vendor Firmware Service)是由獨立法人運作的免費服務,而與 $OS_RELEASE:NAME$ 沒有關聯。您的系統散布商可能尚未驗證過任何韌體更新與您系統間或連接裝置上的相容性。所有本服務中的韌體僅由原始設備製造商提供。" + +#. 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 "這個遠端站點包含未列入禁運,但仍處於硬體廠商測試階段的韌體。您應該確保自己在韌體更新失敗時有方法能夠手動降級韌體。" #. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" msgid "Title" @@ -655,6 +797,10 @@ msgid "Type" msgstr "類型" +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI 韌體公用程式" + #. TRANSLATORS: section header for firmware URI msgid "URI" msgstr "URI" @@ -670,6 +816,10 @@ msgid "Unlocks the device for firmware access" msgstr "解鎖裝置以供韌體存取" +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "取消更新期間的除錯旗標設定" + #. TRANSLATORS: section header for firmware checksum msgid "Update Checksum" msgstr "更新校驗計算碼" @@ -698,9 +848,6 @@ 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 瞭解更多資訊:" @@ -725,7 +872,7 @@ #. * version numbers e.g. "1.2.3" #, c-format msgid "Updating %s from %s to %s... " -msgstr "正更新 %s 從 %s 版至 %s 版…" +msgstr "正將 %s 從 %s 版升級至 %s 版... " #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" @@ -735,6 +882,10 @@ msgid "Upload report now?" msgstr "是否立刻上傳報告?" +#. TRANSLATORS: explain why we want to upload +msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +msgstr "上傳韌體報告可以協助硬體廠商快速辨識出更新作業在真實裝置上是失敗還成功。" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "使用者名稱" @@ -756,6 +907,10 @@ msgstr "監看 DFU 裝置熱插拔的過程" #. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "監看硬體是否有變更" + +#. TRANSLATORS: command description msgid "Write firmware from file into device" msgstr "從檔案將韌體寫入裝置" @@ -767,5 +922,6 @@ msgid "Writing…" msgstr "寫入中…" -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 "您的系統散布商可能尚未驗證過任何韌體更新與您系統間或連接裝置上的相容性。" diff -Nru fwupd-1.0.9/policy/org.freedesktop.fwupd.policy.in fwupd-1.2.10/policy/org.freedesktop.fwupd.policy.in --- fwupd-1.0.9/policy/org.freedesktop.fwupd.policy.in 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/policy/org.freedesktop.fwupd.policy.in 2019-07-15 18:25:54.000000000 +0000 @@ -90,6 +90,28 @@ + + Modify daemon configuration + + Authentication is required to modify daemon configuration + + auth_admin + no + auth_admin_keep + + + + + Activate the new firmware on the device + + Authentication is required to switch to the new firmware version + + auth_admin + no + auth_admin_keep + + + Update the stored device verification information @@ -108,6 +130,28 @@ auth_admin no + auth_admin_keep + + + + + Sets the list of approved firmware + + Authentication is required to set the list of approved firmware + + auth_admin + no + auth_admin_keep + + + + + Sign data using the client certificate + + Authentication is required to sign data using the client certificate + + auth_admin + no auth_admin_keep diff -Nru fwupd-1.0.9/README.md fwupd-1.2.10/README.md --- fwupd-1.0.9/README.md 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -3,6 +3,8 @@ [![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 @@ -70,17 +72,44 @@ Only updates that were distributed from the LVFS will be reported to the LVFS. +Enterprise Use +-------------- + +The flow of updates can be controlled in the enterprise using the +"approved updates" feature. This allows the domain administrator to filter +the possible updates from a central server (e.g. the LVFS, or a mirror) +to only firmware that have been tested specifically in your organisation. + +The list of approved updates can be enabled by adding `ApprovalRequired=true` +to the remote configuration file, e.g. `lvfs.conf`. Once enabled, the +list of approved updates can be set in `daemon.conf` using a comma delimited list. + +For example: + + ApprovedFirmware=foo,bar + +Where `foo,bar` refers to the container checksums that would correspond +to two updates in the metadata file. + +Additionally, the list of approved firmware can be supplemented using +`fwupdmgr set-approved-firmware baz` or using the D-Bus interface. + 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. - -After the firmware has been downloaded a popup will be displayed in Gnome Software -to perform the update. - -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. + 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. + +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. + +3. [Wyse Management Suite](https://www.dell.com/en-us/work/shop/wyse-endpoints-and-software/wyse-management-suite/spd/wyse-wms) + A software suite available on Dell IoT gateways and Wyse thin clients with built-in fwupd support. + The remote administration interface can be used to download and deploy firmware + updates. diff -Nru fwupd-1.0.9/RELEASE fwupd-1.2.10/RELEASE --- fwupd-1.0.9/RELEASE 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/RELEASE 2019-07-15 18:25:54.000000000 +0000 @@ -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.0.8.. | grep -i -v trivial | grep -v Merge > NEWS.new - -Version 1.0.9 -~~~~~~~~~~~~~ -Released: 2018-xx-xx - -New Features: -Bugfixes: +git log --format="%s" --cherry-pick --right-only 1.2.9... | 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 + +Update translations: + +ninja-build fwupd-pot +tx push --source +tx pull --all --force --minimum-perc=5 +ninja-build fix-translations +git add ../po/*.po 2. Commit changes to git: -# MAKE SURE THESE ARE CORRECT -export release_ver="1.0.9" +# MAKE SURE THIS IS CORRECT +export release_ver="1.2.10" git commit -a -m "Release fwupd ${release_ver}" git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" @@ -26,7 +29,7 @@ ninja dist -3a. Generate the additon verification metadata +3a. Generate the additional verification metadata gpg -b -a meson-dist/fwupd-${release_ver}.tar.xz diff -Nru fwupd-1.0.9/SECURITY.md fwupd-1.2.10/SECURITY.md --- fwupd-1.0.9/SECURITY.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/SECURITY.md 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,34 @@ +# Security Policy + +Due to the nature of what we are doing, fwupd takes security very seriously. +If you have any concerns please let us know. + +## Supported Versions + +The `1.2.x` and `1.1.x` branches are fully supported by the upstream authors. +Additonally, the `1.0.x` branch is supported for security and bug fixes. + +Older releases than this are unsupported by upstream but may be supported by +your distributor or distribution. If you open an issue with one of these older +releases the very first question from us is going to be asking if it's fixed on +a supported branch. You can use the flatpak or snap packages if your distributor +is unwilling to update to a supported version. + +| Version | Supported | +| ------- | ------------------ | +| 1.2.x | :heavy_check_mark: | +| 1.1.x | :heavy_check_mark: | +| 1.0.x | :white_check_mark: | +| 0.9.x | :x: | +| 0.8.x | :x: | + +## Reporting a Vulnerability + +If you find a vulnerability in fwupd your first thing you should do is email +all the maintainers, which are currently listed in the `MAINTAINERS` file in +this repository. + +Failing that, please report the issue against the `fwupd` component in Red Hat +bugzilla, with the security checkbox set. You should get a response within 3 +days. We have no bug bountry program, but we're happy to credit you in updates +if this is what you would like us to do. diff -Nru fwupd-1.0.9/snap/hooks/install fwupd-1.2.10/snap/hooks/install --- fwupd-1.0.9/snap/hooks/install 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/snap/hooks/install 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,21 @@ +#!/bin/sh -e + +install_if_missing() { + if [ "$2" != "/" ]; then + mkdir -p $(dirname ${2}/${1}) + fi + install -m 644 -C ${SNAP}/${1} ${2}/${1} +} + +#install policykit rules and actions +install_if_missing share/polkit-1/actions/org.freedesktop.fwupd.policy /usr +install_if_missing share/polkit-1/rules.d/org.freedesktop.fwupd.rules /usr +#install dbus related items +install_if_missing share/dbus-1/system-services/org.freedesktop.fwupd.service /usr +install_if_missing share/dbus-1/interfaces/org.freedesktop.fwupd.xml /usr +install_if_missing etc/dbus-1/system.d/org.freedesktop.fwupd.conf / +#activation via systemd +install_if_missing etc/systemd/system/fwupd-activate.service / +systemctl daemon-reload +systemctl enable fwupd-activate +systemctl start fwupd-activate diff -Nru fwupd-1.0.9/snap/hooks/remove fwupd-1.2.10/snap/hooks/remove --- fwupd-1.0.9/snap/hooks/remove 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/snap/hooks/remove 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,7 @@ +#!/bin/sh -e + +#activation via systemd +systemctl stop fwupd-activate +systemctl disable fwupd-activate +rm /etc/systemd/system/fwupd-activate.service -f +systemctl daemon-reload diff -Nru fwupd-1.0.9/snap/snapcraft.yaml fwupd-1.2.10/snap/snapcraft.yaml --- fwupd-1.0.9/snap/snapcraft.yaml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/snap/snapcraft.yaml 2019-07-15 18:25:54.000000000 +0000 @@ -6,36 +6,41 @@ 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: devel -confinement: devmode +grade: stable +confinement: classic architectures: - amd64 apps: + dfu-tool: + command: dfu-tool.wrapper fwupdtool: command: fwupdtool.wrapper completer: - usr/share/bash-completion/completions/fwupdtool - -plugs: - fwupdtool: - interface: home + 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 - source: https://github.com/rhboot/efivar/releases/download/35/efivar-35.tar.bz2 + make-parameters: + - prefix=/ + - libdir=/lib + source: https://github.com/rhboot/efivar/releases/download/37/efivar-37.tar.bz2 build-packages: - libpopt-dev - organize: - usr/lib64: usr/lib/x86_64-linux-gnu - usr/lib: usr/lib/i386-linux-gnu - prime: - - -usr/include - - -usr/bin - - -usr/share/man - - -usr/lib/*/pkgconfig + prime: + - -include + - -bin + - -share/man + - -lib/pkgconfig #adjust the paths from libefivar libefivar-fixpkgconfig: plugin: make @@ -61,35 +66,9 @@ - -share/ - -etc/ - -lib/*.a - libfwup-dev: - plugin: make - #fwupdate 11 FTBFS: see https://github.com/rhboot/fwupdate/pull/113 - source: https://github.com/rhboot/fwupdate.git - source-type: git - make-parameters: - - EFIDIR=ubuntu - - GNUEFIDIR=/usr/lib - - LIBDIR=$SNAPCRAFT_STAGE/usr/lib/x86_64-linux-gnu - - libdir=/usr/lib/x86_64-linux-gnu - - PKG_CONFIG_PATH=$SNAPCRAFT_STAGE/usr/lib/x86_64-linux-gnu/pkgconfig - - --eval=export PKG_CONFIG_PATH - build-packages: - - elfutils - - gnu-efi - - libasm1 - - libdw1 - prime: - - -usr/lib/debug - - -usr/lib/systemd - - -usr/src - - -usr/share - - -usr/lib/*/pkgconfig - - -usr/include - - -usr/bin - after: [libsmbios, libefivar-fixpkgconfig] meson: plugin: python - source: https://github.com/mesonbuild/meson/releases/download/0.46.1/meson-0.46.1.tar.gz + source: https://github.com/mesonbuild/meson/releases/download/0.47.2/meson-0.47.2.tar.gz build-packages: - ninja-build prime: @@ -98,53 +77,6 @@ - -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 @@ -161,6 +93,68 @@ - -lib/girepository-1.0 - -lib/pkgconfig - -share/ + # this is for the library only, we don't care about the daemon "in-snap" + modemmanager: + plugin: autotools + source: https://gitlab.freedesktop.org/mobile-broadband/ModemManager.git + source-tag: 1.10.0 + after: [gudev, gettext] + # build without these; system daemon needs them + configflags: + - --without-mbim + - --without-qmi + prime: + - -include + - -etc + - -sbin + - -bin + - -share + - -lib/*.*a + - -lib/pkgconfig + - -lib/ModemManager + - -lib/systemd + - -lib/udev + - -lib/girepository-1.0 + libmbim: + plugin: autotools + source: https://gitlab.freedesktop.org/mobile-broadband/libmbim.git + source-tag: 1.18.0 + after: [gudev, gettext, modemmanager] + # build without these; system daemon needs them + configflags: + - --without-udev + prime: + - -include + - -etc + - -sbin + - -bin + - -share + - -lib/*.*a + - -lib/pkgconfig + - -lib/ModemManager + - -lib/systemd + - -lib/udev + - -lib/girepository-1.0 + libqmi: + plugin: autotools + source: https://gitlab.freedesktop.org/mobile-broadband/libqmi.git + source-tag: 1.23.1 + after: [gudev, gettext, modemmanager, libmbim] + # build without these; system daemon needs them + configflags: + - --without-udev + prime: + - -include + - -etc + - -sbin + - -bin + - -share + - -lib/*.*a + - -lib/pkgconfig + - -lib/ModemManager + - -lib/systemd + - -lib/udev + - -lib/girepository-1.0 libusb: plugin: autotools source: https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2 @@ -172,60 +166,136 @@ gusb: plugin: meson source: https://github.com/hughsie/libgusb/archive/0.3.0.tar.gz - meson-parameters: [--prefix=/usr, + 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: - - -usr/bin/ + - -**/*.a + - -**/*.la + - -usr/bin - -usr/include + - -usr/lib/gettext - -usr/share - - -usr/lib/*/pkgconfig - - -usr/lib/*/girepository-1.0 - after: [libusb] fwupd: plugin: meson - meson-parameters: [--prefix=/usr, - --libexecdir=/usr/lib, + 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=false, + -Ddaemon=true, -Dgtkdoc=false, - -Dplugin_uefi_labels=false, -Dintrospection=false, - -Dsystemd=false, -Dman=false, - -Dconsolekit=false, - -Dpkcs7=false, - -Dgpg=false] + -Dplugin_modem_manager=true, + -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) > $SNAPCRAFT_STAGE/version + echo $(git describe HEAD --always) > $SNAPCRAFT_STAGE/version build-packages: - bash-completion - gcab - - gettext + - gnutls-dev - libarchive-dev + - libcairo-dev - libelf-dev + - libftdi1-dev - libgcab-dev - libglib2.0-dev - libgpgme11-dev - libjson-glib-dev + - libpango1.0-dev + - libpci-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/lib/gnupg - -usr/share/man - -usr/share/GConf - -etc/X11 @@ -240,13 +310,9 @@ - -usr/lib/*/audit - -usr/share/glib-2.0/schemas - -usr/share/X11 - - -usr/include - - -etc/dbus-1 - - -lib/systemd + - -include - -lib/udev - - -usr/lib/fwupd/fwupd - - -usr/share/dbus-1 - - -usr/share/gnupg + - -lib/*/pkgconfig - -usr/share/lintian - -usr/share/pkgconfig - -usr/share/installed-tests @@ -257,13 +323,16 @@ - -usr/share/info - -usr/share/gir-1.0 - -usr/share/upstart - - -usr/lib/*/glib-2.0 - -usr/lib/*/pkgconfig - after: [appstream-glib-dev, libfwup-dev, gudev, gusb] + after: [gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext, modemmanager, libmbim, libqmi] fix-bash-completion: plugin: make source: contrib/snap/fix-bash-completion after: [fwupd] + activate-shutdown: + plugin: make + source: contrib/snap/activate-shutdown + after: [fwupd] update-mime: plugin: make source: contrib/snap/update-mime @@ -280,8 +349,12 @@ - -usr/share/pkgconfig - -usr/share/GConf after: [fwupd] - fwupdtool-wrapper: + fwupd-wrappers: plugin: dump source: contrib/snap stage: + - dfu-tool.wrapper + - fwupd-command - fwupdtool.wrapper + - fwupd.wrapper + - fwupdmgr.wrapper diff -Nru fwupd-1.0.9/src/fu-agent.c fwupd-1.2.10/src/fu-agent.c --- fwupd-1.0.9/src/fu-agent.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-agent.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuAgent" + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "fu-common.h" +#include "fu-util-common.h" +#include "fwupd-device-private.h" +#include "fwupd-enums-private.h" + +struct FuUtilPrivate { + GCancellable *cancellable; + GMainLoop *loop; + GOptionContext *context; + FwupdClient *client; +}; + +static gboolean +fu_util_add_devices_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error) +{ + g_autoptr(GPtrArray) devs = NULL; + + /* get results from daemon */ + devs = fwupd_client_get_devices (priv->client, priv->cancellable, error); + if (devs == NULL) + return FALSE; + + json_builder_set_member_name (builder, "Devices"); + json_builder_begin_array (builder); + for (guint i = 0; i < devs->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devs, i); + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + + /* add all releases that could be applied */ + rels = fwupd_client_get_releases (priv->client, + fwupd_device_get_id (dev), + priv->cancellable, + &error_local); + if (rels == NULL) { + g_debug ("not adding releases to device: %s", + error_local->message); + } else { + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel = g_ptr_array_index (rels, j); + fwupd_device_add_release (dev, rel); + } + } + + /* add to builder */ + json_builder_begin_object (builder); + fwupd_device_to_json (dev, builder); + json_builder_end_object (builder); + } + json_builder_end_array (builder); + return TRUE; +} + +static gboolean +fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + /* check args */ + if (g_strv_length (values) != 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* create header */ + builder = json_builder_new (); + json_builder_begin_object (builder); + if (!fu_util_add_devices_json (priv, builder, error)) + return FALSE; + json_builder_end_object (builder); + + /* export as a string */ + json_root = json_builder_get_root (builder); + json_generator = json_generator_new (); + json_generator_set_pretty (json_generator, TRUE); + json_generator_set_root (json_generator, json_root); + data = json_generator_to_data (json_generator, NULL); + if (data == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert to JSON string"); + return FALSE; + } + + /* just print */ + g_print ("%s\n", data); + return TRUE; +} + +static void +fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer user_data) +{ +} + +static gboolean +fu_util_sigint_cb (gpointer user_data) +{ + FuUtilPrivate *priv = (FuUtilPrivate *) user_data; + g_debug ("Handling SIGINT"); + g_cancellable_cancel (priv->cancellable); + return FALSE; +} + +static void +fu_util_private_free (FuUtilPrivate *priv) +{ + if (priv->client != NULL) + g_object_unref (priv->client); + g_main_loop_unref (priv->loop); + g_object_unref (priv->cancellable); + g_option_context_free (priv->context); + g_free (priv); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) +#pragma clang diagnostic pop + +int +main (int argc, char *argv[]) +{ + gboolean ret; + gboolean verbose = FALSE; + g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new (); + g_autofree gchar *cmd_descriptions = NULL; + const GOptionEntry options[] = { + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + /* TRANSLATORS: command line option */ + _("Show extra debugging information"), NULL }, + { NULL} + }; + + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + /* ensure D-Bus errors are registered */ + fwupd_error_quark (); + + /* create helper object */ + priv->loop = g_main_loop_new (NULL, FALSE); + priv->client = fwupd_client_new (); + + /* add commands */ + fu_util_cmd_array_add (cmd_array, + "get-devices", NULL, + /* TRANSLATORS: command description */ + _("Get all devices and possible releases"), + fu_util_get_devices); + + /* sort by command name */ + fu_util_cmd_array_sort (cmd_array); + + /* do stuff on ctrl+c */ + priv->cancellable = g_cancellable_new (); + g_unix_signal_add_full (G_PRIORITY_DEFAULT, + SIGINT, fu_util_sigint_cb, + priv, NULL); + + /* get a list of the commands */ + priv->context = g_option_context_new (NULL); + cmd_descriptions = fu_util_cmd_array_to_string (cmd_array); + g_option_context_set_summary (priv->context, cmd_descriptions); + g_option_context_set_description (priv->context, + "This tool can be used from other tools and from shell scripts."); + + /* TRANSLATORS: program name */ + g_set_application_name (_("Firmware Agent")); + g_option_context_add_main_entries (priv->context, options, NULL); + ret = g_option_context_parse (priv->context, &argc, &argv, &error); + if (!ret) { + /* TRANSLATORS: the user didn't read the man page */ + g_print ("%s: %s\n", _("Failed to parse arguments"), + error->message); + return EXIT_FAILURE; + } + + /* set verbose? */ + if (verbose) { + g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); + } else { + g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + fu_util_ignore_cb, NULL); + } + + /* run the specified command */ + ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error); + if (!ret) { + if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { + g_autofree gchar *tmp = NULL; + tmp = g_option_context_get_help (priv->context, TRUE, NULL); + g_print ("%s\n\n%s", error->message, tmp); + return EXIT_FAILURE; + } + g_print ("%s\n", error->message); + return EXIT_FAILURE; + } + + /* success */ + return EXIT_SUCCESS; +} diff -Nru fwupd-1.0.9/src/fu-archive.c fwupd-1.2.10/src/fu-archive.c --- fwupd-1.0.9/src/fu-archive.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-archive.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/src/fu-archive.h fwupd-1.2.10/src/fu-archive.h --- fwupd-1.0.9/src/fu-archive.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-archive.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define FU_TYPE_ARCHIVE (fu_archive_get_type ()) + +G_DECLARE_FINAL_TYPE (FuArchive, fu_archive, FU, ARCHIVE, GObject) + +/** + * FuArchiveFlags: + * @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. + * @bytes: 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 diff -Nru fwupd-1.0.9/src/fu-chunk.c fwupd-1.2.10/src/fu-chunk.c --- fwupd-1.0.9/src/fu-chunk.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-chunk.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuChunk" + +#include "config.h" + +#include + +#include "fu-chunk.h" + +/** + * fu_chunk_new: + * @idx: the packet number + * @page: the hardware memory page + * @address: the address *within* the page + * @data: the data + * @data_sz: size of @data_sz + * + * Creates a new packet of chunked data. + * + * Return value: (transfer full): a #FuChunk + **/ +FuChunk * +fu_chunk_new (guint32 idx, + guint32 page, + guint32 address, + const guint8 *data, + guint32 data_sz) +{ + FuChunk *item = g_new0 (FuChunk, 1); + item->idx = idx; + item->page = page; + item->address = address; + item->data = data; + item->data_sz = data_sz; + return item; +} + +/** + * fu_chunk_to_string: + * @item: a #FuChunk + * + * Converts the chunked packet to a string representation. + * + * Return value: (transfer full): A string + **/ +gchar * +fu_chunk_to_string (FuChunk *item) +{ + g_autoptr(GString) str = g_string_new (NULL); + if (item->data != NULL) { + for (guint32 i = 0; i < item->data_sz; i++) { + gchar tmp = (gchar) item->data[i]; + if (tmp == 0x00) + break; + g_string_append_c (str, g_ascii_isalnum (tmp) ? tmp : '?'); + } + } + return g_strdup_printf ("#%02" G_GUINT32_FORMAT ": page:%02x " + "addr:%04x len:%02" G_GUINT32_FORMAT " %s", + item->idx, + (guint) item->page, + (guint) item->address, + item->data_sz, + str->str); +} + +/** + * fu_chunk_array_to_string: + * @chunks: (element-type FuChunk): array of packets + * + * Converts all the chunked packets in an array to a string representation. + * + * Return value: (transfer full): A string + **/ +gchar * +fu_chunk_array_to_string (GPtrArray *chunks) +{ + GString *str = g_string_new (NULL); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *item = g_ptr_array_index (chunks, i); + g_autofree gchar *tmp = fu_chunk_to_string (item); + g_string_append_printf (str, "%s\n", tmp); + } + return g_string_free (str, FALSE); +} + +/** + * fu_chunk_array_new: + * @data: a linear blob of memory, or %NULL + * @data_sz: size of @data_sz + * @addr_start: the hardware address offset, or 0 + * @page_sz: the hardware page size, or 0 + * @packet_sz: the transfer size, or 0 + * + * Chunks a linear blob of memory into packets, ensuring each packet does not + * cross a package boundary and is less that a specific transfer size. + * + * Return value: (element-type FuChunk): array of packets + **/ +GPtrArray * +fu_chunk_array_new (const guint8 *data, + guint32 data_sz, + guint32 addr_start, + guint32 page_sz, + guint32 packet_sz) +{ + GPtrArray *segments = NULL; + guint32 page_old = G_MAXUINT32; + guint32 idx; + guint32 last_flush = 0; + + g_return_val_if_fail (data_sz > 0, NULL); + + segments = g_ptr_array_new_with_free_func (g_free); + for (idx = 1; idx < data_sz; idx++) { + guint32 page = 0; + if (page_sz > 0) + page = (addr_start + idx) / page_sz; + if (page_old == G_MAXUINT32) { + page_old = page; + } else if (page != page_old) { + const guint8 *data_offset = data != NULL ? data + last_flush : 0x0; + guint32 address_offset = addr_start + last_flush; + if (page_sz > 0) + address_offset %= page_sz; + g_ptr_array_add (segments, + fu_chunk_new (segments->len, + page_old, + address_offset, + data_offset, + idx - last_flush)); + last_flush = idx; + page_old = page; + continue; + } + if (packet_sz > 0 && idx - last_flush >= packet_sz) { + const guint8 *data_offset = data != NULL ? data + last_flush : 0x0; + guint32 address_offset = addr_start + last_flush; + if (page_sz > 0) + address_offset %= page_sz; + g_ptr_array_add (segments, + fu_chunk_new (segments->len, + page, + address_offset, + data_offset, + idx - last_flush)); + last_flush = idx; + continue; + } + } + if (last_flush != idx) { + const guint8 *data_offset = data != NULL ? data + last_flush : 0x0; + guint32 address_offset = addr_start + last_flush; + guint32 page = 0; + if (page_sz > 0) { + address_offset %= page_sz; + page = (addr_start + (idx - 1)) / page_sz; + } + g_ptr_array_add (segments, + fu_chunk_new (segments->len, + page, + address_offset, + data_offset, + data_sz - last_flush)); + } + return segments; +} + +/** + * fu_chunk_array_new_from_bytes: + * @blob: a #GBytes + * @addr_start: the hardware address offset, or 0 + * @page_sz: the hardware page size, or 0 + * @packet_sz: the transfer size, or 0 + * + * Chunks a linear blob of memory into packets, ensuring each packet does not + * cross a package boundary and is less that a specific transfer size. + * + * Return value: (element-type FuChunk): array of packets + **/ +GPtrArray * +fu_chunk_array_new_from_bytes (GBytes *blob, + guint32 addr_start, + guint32 page_sz, + guint32 packet_sz) +{ + gsize sz; + const guint8 *data = g_bytes_get_data (blob, &sz); + return fu_chunk_array_new (data, (guint32) sz, + addr_start, page_sz, packet_sz); +} diff -Nru fwupd-1.0.9/src/fu-chunk.h fwupd-1.2.10/src/fu-chunk.h --- fwupd-1.0.9/src/fu-chunk.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-chunk.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +typedef struct { + guint32 idx; + guint32 page; + guint32 address; + const guint8 *data; + guint32 data_sz; +} FuChunk; + +FuChunk *fu_chunk_new (guint32 idx, + guint32 page, + guint32 address, + const guint8 *data, + guint32 data_sz); +gchar *fu_chunk_to_string (FuChunk *item); + +gchar *fu_chunk_array_to_string (GPtrArray *chunks); +GPtrArray *fu_chunk_array_new (const guint8 *data, + guint32 data_sz, + guint32 addr_start, + guint32 page_sz, + guint32 packet_sz); +GPtrArray *fu_chunk_array_new_from_bytes (GBytes *blob, + guint32 addr_start, + guint32 page_sz, + guint32 packet_sz); + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-common.c fwupd-1.2.10/src/fu-common.c --- fwupd-1.0.9/src/fu-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuCommon" + #include #include @@ -13,7 +14,9 @@ #include #include #include +#include #include +#include #include "fwupd-error.h" @@ -350,6 +353,47 @@ g_ptr_array_add (argv, g_strdup (split[i])); } +gchar * +fu_common_find_program_in_path (const gchar *basename, GError **error) +{ + gchar *fn = g_find_program_in_path (basename); + if (fn == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing executable %s in PATH", + basename); + return NULL; + } + return fn; +} + +static gboolean +fu_common_test_namespace_support (GError **error) +{ + /* test if CONFIG_USER_NS is valid */ + if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing CONFIG_USER_NS in kernel"); + return FALSE; + } + if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) { + g_autofree gchar *clone = NULL; + if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error)) + return FALSE; + if (g_ascii_strtoll (clone, NULL, 10) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unprivileged user namespace clones disabled by distro"); + return FALSE; + } + } + return TRUE; +} + /** * fu_common_firmware_builder: * @bytes: The data to use @@ -376,6 +420,7 @@ { gint rc = 0; g_autofree gchar *argv_str = NULL; + g_autofree gchar *bwrap_fn = NULL; g_autofree gchar *localstatebuilderdir = NULL; g_autofree gchar *localstatedir = NULL; g_autofree gchar *output2_fn = NULL; @@ -390,6 +435,15 @@ g_return_val_if_fail (output_fn != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); + /* find bwrap in the path */ + bwrap_fn = fu_common_find_program_in_path ("bwrap", error); + if (bwrap_fn == NULL) + return NULL; + + /* test if CONFIG_USER_NS is valid */ + if (!fu_common_test_namespace_support (error)) + return NULL; + /* untar file to temp location */ tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error); if (tmpdir == NULL) @@ -402,7 +456,7 @@ localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL); /* launch bubblewrap and generate firmware */ - g_ptr_array_add (argv, g_strdup ("bwrap")); + g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn)); fu_common_add_argv (argv, "--die-with-parent"); fu_common_add_argv (argv, "--ro-bind /usr /usr"); fu_common_add_argv (argv, "--ro-bind /lib /lib"); @@ -436,9 +490,12 @@ if (standard_output != NULL && standard_output[0] != '\0') g_debug ("console output was: %s", standard_output); if (rc != 0) { + FwupdError code = FWUPD_ERROR_INTERNAL; + if (errno == ENOTTY) + code = FWUPD_ERROR_PERMISSION_DENIED; g_set_error (error, FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, + code, "failed to build firmware: %s", standard_error); return NULL; @@ -465,6 +522,7 @@ GSource *source; GInputStream *stream; GCancellable *cancellable; + guint timeout_id; } FuCommonSpawnHelper; static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper); @@ -526,12 +584,15 @@ static void fu_common_spawn_helper_free (FuCommonSpawnHelper *helper) { + g_object_unref (helper->cancellable); if (helper->stream != NULL) g_object_unref (helper->stream); if (helper->source != NULL) g_source_destroy (helper->source); if (helper->loop != NULL) g_main_loop_unref (helper->loop); + if (helper->timeout_id != 0) + g_source_remove (helper->timeout_id); g_free (helper); } @@ -540,11 +601,29 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free) #pragma clang diagnostic pop +static gboolean +fu_common_spawn_timeout_cb (gpointer user_data) +{ + FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data; + g_cancellable_cancel (helper->cancellable); + g_main_loop_quit (helper->loop); + helper->timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static void +fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper) +{ + /* just propagate */ + g_cancellable_cancel (helper->cancellable); +} + /** * fu_common_spawn_sync: * @argv: The argument list to run * @handler_cb: (scope call): A #FuOutputHandler or %NULL * @handler_user_data: the user data to pass to @handler_cb + * @timeout_ms: a timeout in ms, or 0 for no limit * @cancellable: a #GCancellable, or %NULL * @error: A #GError or %NULL * @@ -557,11 +636,13 @@ fu_common_spawn_sync (const gchar * const * argv, FuOutputHandler handler_cb, gpointer handler_user_data, + guint timeout_ms, GCancellable *cancellable, GError **error) { g_autoptr(FuCommonSpawnHelper) helper = NULL; g_autoptr(GSubprocess) subprocess = NULL; g_autofree gchar *argv_str = NULL; + gulong cancellable_id = 0; /* create subprocess */ argv_str = g_strjoinv (" ", (gchar **) argv); @@ -577,9 +658,26 @@ helper->handler_user_data = handler_user_data; helper->loop = g_main_loop_new (NULL, FALSE); helper->stream = g_subprocess_get_stdout_pipe (subprocess); - helper->cancellable = cancellable; + + /* always create a cancellable, and connect up the parent */ + helper->cancellable = g_cancellable_new (); + if (cancellable != NULL) { + cancellable_id = g_cancellable_connect (cancellable, + G_CALLBACK (fu_common_spawn_cancelled_cb), + helper, NULL); + } + + /* allow timeout */ + if (timeout_ms > 0) { + helper->timeout_id = g_timeout_add (timeout_ms, + fu_common_spawn_timeout_cb, + helper); + } fu_common_spawn_create_pollable_source (helper); g_main_loop_run (helper->loop); + g_cancellable_disconnect (cancellable, cancellable_id); + if (g_cancellable_set_error_if_cancelled (helper->cancellable, error)) + return FALSE; return g_subprocess_wait_check (subprocess, cancellable, error); } @@ -687,6 +785,62 @@ return val_native; } +/** + * fu_common_strtoull: + * @str: A string, e.g. "0x1234" + * + * Converts a string value to an integer. Values are assumed base 10, unless + * prefixed with "0x" where they are parsed as base 16. + * + * Returns: integer value, or 0x0 for error + **/ +guint64 +fu_common_strtoull (const gchar *str) +{ + guint base = 10; + if (str == NULL) + return 0x0; + if (g_str_has_prefix (str, "0x")) { + str += 2; + base = 16; + } + return g_ascii_strtoull (str, NULL, base); +} + +/** + * fu_common_strstrip: + * @str: A string, e.g. " test " + * + * Removes leading and trailing whitespace from a constant string. + * + * Returns: newly allocated string + **/ +gchar * +fu_common_strstrip (const gchar *str) +{ + guint head = G_MAXUINT; + guint tail = 0; + + g_return_val_if_fail (str != NULL, NULL); + + /* find first non-space char */ + for (guint i = 0; str[i] != '\0'; i++) { + if (str[i] != ' ') { + head = i; + break; + } + } + if (head == G_MAXUINT) + return g_strdup (""); + + /* find last non-space char */ + for (guint i = head; str[i] != '\0'; i++) { + if (str[i] != ' ') + tail = i; + } + return g_strndup (str + head, tail - head + 1); +} + static const GError * fu_common_error_array_find (GPtrArray *errors, FwupdError error_code) { @@ -811,6 +965,24 @@ if (tmp != NULL) return g_build_filename (tmp, LOCALSTATEDIR, NULL); return g_build_filename (LOCALSTATEDIR, NULL); + /* /sys/firmware */ + case FU_PATH_KIND_SYSFSDIR_FW: + tmp = g_getenv ("FWUPD_SYSFSFWDIR"); + if (tmp != NULL) + return g_strdup (tmp); + return g_strdup ("/sys/firmware"); + /* /sys/class/tpm */ + 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"); + if (tmp != NULL) + return g_strdup (tmp); + return g_strdup ("/sys/bus/platform/drivers"); /* /etc */ case FU_PATH_KIND_SYSCONFDIR: tmp = g_getenv ("FWUPD_SYSCONFDIR"); @@ -838,6 +1010,19 @@ if (tmp != NULL) return g_build_filename (tmp, DATADIR, PACKAGE_NAME, NULL); return g_build_filename (DATADIR, PACKAGE_NAME, NULL); + /* /usr/libexec/fwupd/efi */ + case FU_PATH_KIND_EFIAPPDIR: + tmp = g_getenv ("FWUPD_EFIAPPDIR"); + if (tmp != NULL) + return g_strdup (tmp); +#ifdef EFI_APP_LOCATION + tmp = g_getenv ("SNAP"); + if (tmp != NULL) + return g_build_filename (tmp, EFI_APP_LOCATION, NULL); + return g_strdup (EFI_APP_LOCATION); +#else + return NULL; +#endif /* /etc/fwupd */ case FU_PATH_KIND_SYSCONFDIR_PKG: basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR); @@ -850,6 +1035,12 @@ case FU_PATH_KIND_CACHEDIR_PKG: basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR); return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL); + case FU_PATH_KIND_POLKIT_ACTIONS: +#ifdef POLKIT_ACTIONDIR + return g_strdup (POLKIT_ACTIONDIR); +#else + return NULL; +#endif /* this shouldn't happen */ default: g_warning ("cannot build path for unknown kind %u", path_kind); @@ -857,3 +1048,314 @@ 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_bytes: + * @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); +} + +/** + * fu_common_bytes_is_empty: + * @bytes: a #GBytes + * + * Checks if a byte array are just empty (0xff) bytes. + * + * Return value: %TRUE if @bytes is empty + **/ +gboolean +fu_common_bytes_is_empty (GBytes *bytes) +{ + gsize sz = 0; + const guint8 *buf = g_bytes_get_data (bytes, &sz); + for (gsize i = 0; i < sz; i++) { + if (buf[i] != 0xff) + return FALSE; + } + return TRUE; +} + +/** + * fu_common_bytes_compare: + * @bytes1: a #GBytes + * @bytes2: another #GBytes + * @error: A #GError or %NULL + * + * Checks if a byte array are just empty (0xff) bytes. + * + * Return value: %TRUE if @bytes1 and @bytes2 are identical + **/ +gboolean +fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error) +{ + const guint8 *buf1; + const guint8 *buf2; + gsize bufsz1; + gsize bufsz2; + + g_return_val_if_fail (bytes1 != NULL, FALSE); + g_return_val_if_fail (bytes2 != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* not the same length */ + buf1 = g_bytes_get_data (bytes1, &bufsz1); + buf2 = g_bytes_get_data (bytes2, &bufsz2); + if (bufsz1 != bufsz2) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got %" G_GSIZE_FORMAT " bytes, expected " + "%" G_GSIZE_FORMAT, bufsz1, bufsz2); + return FALSE; + } + + /* check matches */ + for (guint i = 0x0; i < bufsz1; i++) { + if (buf1[i] != buf2[i]) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got 0x%02x, expected 0x%02x @ 0x%04x", + buf1[i], buf2[i], i); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +/** + * fu_common_realpath: + * @filename: a filename + * @error: A #GError or %NULL + * + * Finds the canonicalized absolute filename for a path. + * + * Return value: A filename, or %NULL if invalid or not found + **/ +gchar * +fu_common_realpath (const gchar *filename, GError **error) +{ + char full_tmp[PATH_MAX]; + + g_return_val_if_fail (filename != NULL, NULL); + + if (realpath (filename, full_tmp) == NULL) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot resolve path: %s", + strerror (errno)); + return NULL; + } + return g_strdup (full_tmp); +} diff -Nru fwupd-1.0.9/src/fu-common-cab.c fwupd-1.2.10/src/fu-common-cab.c --- fwupd-1.0.9/src/fu-common-cab.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-common-cab.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuCommonCab" + #include "config.h" #include @@ -14,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) @@ -22,50 +25,82 @@ 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_extract_name (cabfile), basename) == 0) + return cabfile; + } +#endif } return NULL; } -/* sets the firmware and signature blobs on AsRelease */ +#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 (AsRelease *release, GCabCabinet *cabinet, GError **error) +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, @@ -75,35 +110,41 @@ } /* 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 */ @@ -112,7 +153,12 @@ 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, @@ -121,7 +167,9 @@ 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); } } @@ -129,22 +177,24 @@ 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, @@ -152,31 +202,10 @@ "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)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "could not parse MetaInfo XML: %s", - 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)) { + 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, @@ -184,32 +213,15 @@ 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); @@ -217,8 +229,8 @@ 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; @@ -231,11 +243,12 @@ 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; @@ -278,34 +291,135 @@ /* 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)); @@ -319,9 +433,30 @@ } /* 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, + FWUPD_ERROR_INVALID_FILE, + 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, @@ -329,6 +464,7 @@ error_local->message); return NULL; } +#endif /* the file callback set an error */ if (helper.error != NULL) { @@ -336,53 +472,65 @@ 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, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "archive contained no valid metadata"); + /* 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; - } - - /* success */ - return g_steal_pointer (&store); -} - -#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); + 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; } - /* did we get any valid AsApps */ - if (as_store_get_size (store) == 0) { - g_set_error_literal (error, + /* 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; + } } - return g_steal_pointer (&store); + + /* success */ + return g_steal_pointer (&silo); } -#endif diff -Nru fwupd-1.0.9/src/fu-common-cab.h fwupd-1.2.10/src/fu-common-cab.h --- fwupd-1.0.9/src/fu-common-cab.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-common-cab.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,17 +1,17 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_COMMON_CAB_H -#define __FU_COMMON_CAB_H +#pragma once + +#include -#include +G_BEGIN_DECLS -AsStore *fu_common_store_from_cab_bytes (GBytes *blob, +XbSilo *fu_common_cab_build_silo (GBytes *blob, guint64 size_max, GError **error); -#endif /* __FU_COMMON_CAB_H */ +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-common-guid.c fwupd-1.2.10/src/fu-common-guid.c --- fwupd-1.0.9/src/fu-common-guid.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-common-guid.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include + +#include "fu-common-guid.h" + +/** + * fu_common_guid_is_plausible: + * @buf: a buffer of data + * + * Checks whether a chunk of memory looks like it could be a GUID. + * + * Returns: TRUE if it looks like a GUID, FALSE if not + * + * Since: 1.2.5 + **/ +gboolean +fu_common_guid_is_plausible (const guint8 *buf) +{ + guint guint_sum = 0; + + for (guint i = 0; i < 16; i++) + guint_sum += buf[i]; + if (guint_sum == 0x00) + return FALSE; + if (guint_sum < 0xff) + return FALSE; + return TRUE; +} diff -Nru fwupd-1.0.9/src/fu-common-guid.h fwupd-1.2.10/src/fu-common-guid.h --- fwupd-1.0.9/src/fu-common-guid.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-common-guid.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +gboolean fu_common_guid_is_plausible (const guint8 *buf); + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-common.h fwupd-1.2.10/src/fu-common.h --- fwupd-1.0.9/src/fu-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,29 +1,76 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_COMMON_H__ -#define __FU_COMMON_H__ +#pragma once #include +G_BEGIN_DECLS + +/** + * FuAppFlags: + * @FU_APP_FLAGS_NONE: No flags set + * @FU_APP_FLAGS_NO_IDLE_SOURCES: Disallow idle sources + * + * The flags to use when loading an application. + **/ typedef enum { FU_APP_FLAGS_NONE = 0, FU_APP_FLAGS_NO_IDLE_SOURCES = 1 << 0, + /*< private >*/ FU_APP_FLAGS_LAST } FuAppFlags; +/** + * FuDumpFlags: + * @FU_DUMP_FLAGS_NONE: No flags set + * @FU_DUMP_FLAGS_SHOW_ASCII: Show ASCII in debugging dumps + * @FU_DUMP_FLAGS_SHOW_ADDRESSES: Show addresses in debugging dumps + * + * The flags to use when configuring debugging + **/ +typedef enum { + FU_DUMP_FLAGS_NONE = 0, + FU_DUMP_FLAGS_SHOW_ASCII = 1 << 0, + FU_DUMP_FLAGS_SHOW_ADDRESSES = 1 << 1, + /*< private >*/ + FU_DUMP_FLAGS_LAST +} FuDumpFlags; + +/** + * FuPathKind: + * @FU_PATH_KIND_CACHEDIR_PKG: The cache directory (IE /var/cache/fwupd) + * @FU_PATH_KIND_DATADIR_PKG: The non-volatile data store (IE /usr/share/fwupd) + * @FU_PATH_KIND_EFIAPPDIR: The location to store EFI apps before install (IE /usr/libexec/fwupd/efi) + * @FU_PATH_KIND_LOCALSTATEDIR: The local state directory (IE /var) + * @FU_PATH_KIND_LOCALSTATEDIR_PKG: The local state directory for the package (IE /var/lib/fwupd) + * @FU_PATH_KIND_PLUGINDIR_PKG: The location to look for plugins for package (IE /usr/lib/[triplet]/fwupd-plugins-3) + * @FU_PATH_KIND_SYSCONFDIR: The configuration location (IE /etc) + * @FU_PATH_KIND_SYSCONFDIR_PKG: The package configuration location (IE /etc/fwupd) + * @FU_PATH_KIND_SYSFSDIR_FW: The sysfs firmware location (IE /sys/firmware) + * @FU_PATH_KIND_SYSFSDIR_DRIVERS: The platform sysfs directory (IE /sys/bus/platform/drivers) + * @FU_PATH_KIND_SYSFSDIR_TPM: The TPM sysfs directory (IE /sys/class/tpm) + * @FU_PATH_KIND_POLKIT_ACTIONS The directory for policy kit actions (IE /usr/share/polkit-1/actions/) + * + * Path types to use when dynamically determining a path at runtime + **/ typedef enum { FU_PATH_KIND_CACHEDIR_PKG, FU_PATH_KIND_DATADIR_PKG, + FU_PATH_KIND_EFIAPPDIR, FU_PATH_KIND_LOCALSTATEDIR, FU_PATH_KIND_LOCALSTATEDIR_PKG, FU_PATH_KIND_PLUGINDIR_PKG, FU_PATH_KIND_SYSCONFDIR, FU_PATH_KIND_SYSCONFDIR_PKG, + FU_PATH_KIND_SYSFSDIR_FW, + FU_PATH_KIND_SYSFSDIR_DRIVERS, + FU_PATH_KIND_SYSFSDIR_TPM, + FU_PATH_KIND_POLKIT_ACTIONS, + /*< private >*/ FU_PATH_KIND_LAST } FuPathKind; @@ -33,10 +80,13 @@ gboolean fu_common_spawn_sync (const gchar * const *argv, FuOutputHandler handler_cb, gpointer handler_user_data, + guint timeout_ms, GCancellable *cancellable, GError **error); gchar *fu_common_get_path (FuPathKind path_kind); +gchar *fu_common_realpath (const gchar *filename, + GError **error); gboolean fu_common_rmtree (const gchar *directory, GError **error); GPtrArray *fu_common_get_files_recursive (const gchar *path, @@ -59,6 +109,30 @@ const gchar *output_fn, GError **error); GError *fu_common_error_array_get_best (GPtrArray *errors); +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); +gboolean fu_common_bytes_is_empty (GBytes *bytes); +gboolean fu_common_bytes_compare (GBytes *bytes1, + GBytes *bytes2, + GError **error); typedef guint FuEndianType; @@ -73,4 +147,8 @@ guint32 fu_common_read_uint32 (const guint8 *buf, FuEndianType endian); -#endif /* __FU_COMMON_H__ */ +guint fu_common_string_replace (GString *string, + const gchar *search, + const gchar *replace); + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-common-version.c fwupd-1.2.10/src/fu-common-version.c --- fwupd-1.0.9/src/fu-common-version.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-common-version.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include + +#include + +#include "fwupd-enums.h" +#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_from_uint32: + * @val: A uint32le version number + * @kind: version kind used for formatting, e.g. %FWUPD_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, FwupdVersionFormat kind) +{ + if (kind == FWUPD_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 == FWUPD_VERSION_FORMAT_TRIPLET) { + /* AA.BB.CCDD */ + return g_strdup_printf ("%u.%u.%u", + (val >> 24) & 0xff, + (val >> 16) & 0xff, + val & 0xffff); + } + if (kind == FWUPD_VERSION_FORMAT_PAIR) { + /* AABB.CCDD */ + return g_strdup_printf ("%u.%u", + (val >> 16) & 0xffff, + val & 0xffff); + } + if (kind == FWUPD_VERSION_FORMAT_NUMBER) { + /* AABBCCDD */ + return g_strdup_printf ("%" G_GUINT32_FORMAT, val); + } + if (kind == FWUPD_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 == FWUPD_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 == FWUPD_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); + } + g_critical ("failed to convert version format %s: %u", + fwupd_version_format_to_string (kind), val); + return NULL; +} + +/** + * fu_common_version_from_uint16: + * @val: A uint16le version number + * @kind: version kind used for formatting, e.g. %FWUPD_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, FwupdVersionFormat kind) +{ + if (kind == FWUPD_VERSION_FORMAT_BCD) { + return g_strdup_printf ("%i.%i", + FU_COMMON_VERSION_DECODE_BCD(val >> 8), + FU_COMMON_VERSION_DECODE_BCD(val)); + } + if (kind == FWUPD_VERSION_FORMAT_PAIR) { + return g_strdup_printf ("%u.%u", + (guint) (val >> 8) & 0xff, + (guint) val & 0xff); + } + if (kind == FWUPD_VERSION_FORMAT_NUMBER) { + return g_strdup_printf ("%" G_GUINT16_FORMAT, val); + } + g_critical ("failed to convert version format %s: %u", + fwupd_version_format_to_string (kind), 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; +} + +static gboolean +fu_common_version_is_valid_semver_char (gchar c) +{ + if (g_ascii_isdigit (c)) + return TRUE; + if (c == '.') + return TRUE; + return FALSE; +} + +/** + * fu_common_version_ensure_semver: + * @version: A version number, e.g. ` V1.2.3 ` + * + * Builds a semver from the possibly crazy version number. + * + * Returns: A version number, e.g. "1.2.3" + * + * Since: 1.2.9 + */ +gchar * +fu_common_version_ensure_semver (const gchar *version) +{ + GString *version_safe = g_string_new (NULL); + for (guint i = 0; version[i] != '\0'; i++) { + if (fu_common_version_is_valid_semver_char (version[i])) + g_string_append_c (version_safe, version[i]); + } + return g_string_free (version_safe, FALSE); +} + +/** + * 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, FWUPD_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 + * %FWUPD_VERSION_FORMAT_UNKNOWN constant is returned. + * + * Returns: A #FwupdVersionFormat, e.g. %FWUPD_VERSION_FORMAT_QUAD + * + * Since: 1.2.0 + */ +FwupdVersionFormat +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 FWUPD_VERSION_FORMAT_UNKNOWN; + + /* no dots, assume just text */ + split = g_strsplit (version, ".", -1); + sz = g_strv_length (split); + if (sz == 1) { + if (g_str_has_prefix (version, "0x")) + version += 2; + if (_g_ascii_is_digits (version)) + return FWUPD_VERSION_FORMAT_NUMBER; + return FWUPD_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 FWUPD_VERSION_FORMAT_PLAIN; + } + + /* the most common formats */ + if (sz == 2) + return FWUPD_VERSION_FORMAT_PAIR; + if (sz == 3) + return FWUPD_VERSION_FORMAT_TRIPLET; + if (sz == 4) + return FWUPD_VERSION_FORMAT_QUAD; + + /* unknown! */ + return FWUPD_VERSION_FORMAT_UNKNOWN; +} + +static FwupdVersionFormat +fu_common_version_convert_base (FwupdVersionFormat fmt) +{ + if (fmt == FWUPD_VERSION_FORMAT_INTEL_ME || + fmt == FWUPD_VERSION_FORMAT_INTEL_ME2) + return FWUPD_VERSION_FORMAT_QUAD; + if (fmt == FWUPD_VERSION_FORMAT_BCD) + return FWUPD_VERSION_FORMAT_PAIR; + return fmt; +} + +gboolean +fu_common_version_verify_format (const gchar *version, + FwupdVersionFormat fmt, + GError **error) +{ + FwupdVersionFormat fmt_base = fu_common_version_convert_base (fmt); + + /* don't touch */ + if (fmt == FWUPD_VERSION_FORMAT_PLAIN) + return TRUE; + + /* nothing we can check for */ + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) { + g_debug ("not checking %s as no version format set", version); + return TRUE; + } + + /* check the base format */ + if (fu_common_version_guess_format (version) != fmt_base) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%s is not a valid %s", + version, + fwupd_version_format_to_string (fmt)); + return FALSE; + } + return TRUE; +} + +/** + * 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 -Nru fwupd-1.0.9/src/fu-common-version.h fwupd-1.2.10/src/fu-common-version.h --- fwupd-1.0.9/src/fu-common-version.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-common-version.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +gint fu_common_vercmp (const gchar *version_a, + const gchar *version_b); +gchar *fu_common_version_from_uint32 (guint32 val, + FwupdVersionFormat kind); +gchar *fu_common_version_from_uint16 (guint16 val, + FwupdVersionFormat kind); +gchar *fu_common_version_parse (const gchar *version); +gchar *fu_common_version_ensure_semver (const gchar *version); +FwupdVersionFormat fu_common_version_guess_format (const gchar *version); +gboolean fu_common_version_verify_format (const gchar *version, + FwupdVersionFormat fmt, + GError **error); +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-config.c fwupd-1.2.10/src/fu-config.c --- fwupd-1.0.9/src/fu-config.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-config.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,13 +1,14 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuConfig" + #include "config.h" -#include +#include #include #include #include @@ -29,9 +30,12 @@ GPtrArray *monitors; GPtrArray *blacklist_devices; GPtrArray *blacklist_plugins; + GPtrArray *approved_firmware; guint64 archive_size_max; - AsStore *store_remotes; + guint idle_timeout; + XbSilo *silo; GHashTable *os_release; + gchar *config_file; }; G_DEFINE_TYPE (FuConfig, fu_config, G_TYPE_OBJECT) @@ -74,7 +78,7 @@ g_autoptr(GError) error = NULL; g_autofree gchar *filename = g_file_get_path (file); g_debug ("%s changed, reloading all configs", filename); - if (!fu_config_load (self, &error)) + if (!fu_config_load (self, FU_CONFIG_LOAD_FLAG_NONE, &error)) g_warning ("failed to rescan config: %s", error->message); } @@ -131,70 +135,26 @@ } 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 * @@ -212,8 +172,10 @@ 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); @@ -247,9 +209,13 @@ 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); } @@ -260,11 +226,11 @@ 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); } @@ -386,25 +352,36 @@ return TRUE; } +gboolean +fu_config_modify_and_save (FuConfig *self, const gchar *key, const gchar *value, GError **error) +{ + g_key_file_set_string (self->keyfile, "fwupd", key, value); + return g_key_file_save_to_file (self->keyfile, self->config_file, error); +} + static gboolean fu_config_load_from_file (FuConfig *self, const gchar *config_file, GError **error) { GFileMonitor *monitor; guint64 archive_size_max; + guint idle_timeout; + g_auto(GStrv) approved_firmware = NULL; g_auto(GStrv) devices = NULL; g_auto(GStrv) plugins = NULL; g_autoptr(GFile) file = NULL; + g_autofree gchar *domains = NULL; /* ensure empty in case we're called from a monitor change */ g_ptr_array_set_size (self->blacklist_devices, 0); g_ptr_array_set_size (self->blacklist_plugins, 0); + g_ptr_array_set_size (self->approved_firmware, 0); g_ptr_array_set_size (self->monitors, 0); g_ptr_array_set_size (self->remotes, 0); g_debug ("loading config values from %s", config_file); if (!g_key_file_load_from_file (self->keyfile, config_file, - G_KEY_FILE_NONE, error)) + G_KEY_FILE_KEEP_COMMENTS, error)) return FALSE; /* set up a notify watch */ @@ -442,6 +419,19 @@ } } + /* get approved firmware */ + approved_firmware = g_key_file_get_string_list (self->keyfile, + "fwupd", + "ApprovedFirmware", + NULL, /* length */ + NULL); + if (approved_firmware != NULL) { + for (guint i = 0; approved_firmware[i] != NULL; i++) { + g_ptr_array_add (self->approved_firmware, + g_strdup (approved_firmware[i])); + } + } + /* get maximum archive size, defaulting to something sane */ archive_size_max = g_key_file_get_uint64 (self->keyfile, "fwupd", @@ -449,43 +439,107 @@ 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; + + /* get the domains to run in verbose */ + domains = g_key_file_get_string (self->keyfile, + "fwupd", + "VerboseDomains", + NULL); + if (domains != NULL && domains[0] != '\0') + g_setenv ("FWUPD_VERBOSE", domains, TRUE); + return TRUE; } -gboolean -fu_config_load (FuConfig *self, GError **error) +static gboolean +fu_config_load_metainfos (XbBuilder *builder, GError **error) { + const gchar *fn; g_autofree gchar *datadir = NULL; - g_autofree gchar *configdir = NULL; g_autofree gchar *metainfo_path = NULL; - g_autofree gchar *config_file = 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, FuConfigLoadFlags flags, GError **error) +{ + const gchar *const *locales = g_get_language_names (); + g_autofree gchar *configdir = NULL; + g_autofree gchar *cachedirpkg = NULL; + g_autofree gchar *xmlbfn = NULL; + g_autoptr(GFile) xmlb = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_SINGLE_LANG | + XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID; g_return_val_if_fail (FU_IS_CONFIG (self), FALSE); /* load the main daemon config file */ configdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR_PKG); - config_file = g_build_filename (configdir, "daemon.conf", NULL); - if (g_file_test (config_file, G_FILE_TEST_EXISTS)) { - if (!fu_config_load_from_file (self, config_file, error)) + self->config_file = g_build_filename (configdir, "daemon.conf", NULL); + if (g_file_test (self->config_file, G_FILE_TEST_EXISTS)) { + if (!fu_config_load_from_file (self, self->config_file, error)) return FALSE; } else { - g_warning ("Daemon configuration %s not found", config_file); + g_warning ("Daemon configuration %s not found", self->config_file); } /* load AppStream about the remotes */ 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]); + +#if LIBXMLB_CHECK_VERSION(0,1,7) + /* on a read-only filesystem don't care about the cache GUID */ + if (flags & FU_CONFIG_LOAD_FLAG_READONLY_FS) + compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; +#endif + + /* 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, compile_flags, NULL, error); + if (self->silo == NULL) + return FALSE; /* load remotes */ if (!fu_config_load_remotes (self, error)) @@ -502,6 +556,13 @@ 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) { @@ -530,6 +591,13 @@ return self->blacklist_plugins; } +GPtrArray * +fu_config_get_approved_firmware (FuConfig *self) +{ + g_return_val_if_fail (FU_IS_CONFIG (self), NULL); + return self->approved_firmware; +} + static void fu_config_class_init (FuConfigClass *klass) { @@ -544,9 +612,9 @@ self->keyfile = g_key_file_new (); self->blacklist_devices = g_ptr_array_new_with_free_func (g_free); self->blacklist_plugins = g_ptr_array_new_with_free_func (g_free); + self->approved_firmware = 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 @@ -556,12 +624,15 @@ 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->approved_firmware); g_ptr_array_unref (self->remotes); g_ptr_array_unref (self->monitors); - g_object_unref (self->store_remotes); + g_free (self->config_file); G_OBJECT_CLASS (fu_config_parent_class)->finalize (obj); } diff -Nru fwupd-1.0.9/src/fu-config.h fwupd-1.2.10/src/fu-config.h --- fwupd-1.0.9/src/fu-config.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-config.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,34 +1,50 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_CONFIG_H -#define __FU_CONFIG_H - -G_BEGIN_DECLS +#pragma once #include #include "fwupd-remote.h" +G_BEGIN_DECLS + #define FU_TYPE_CONFIG (fu_config_get_type ()) G_DECLARE_FINAL_TYPE (FuConfig, fu_config, FU, CONFIG, GObject) +/** + * FuConfigLoadFlags: + * @FU_CONFIG_LOAD_FLAG_NONE: No flags set + * @FU_CONFIG_LOAD_FLAG_READONLY_FS: Ignore readonly filesystem errors + * + * The flags to use when loading a configuration file. + **/ +typedef enum { + FU_CONFIG_LOAD_FLAG_NONE = 0, + FU_CONFIG_LOAD_FLAG_READONLY_FS = 1 << 0, + /*< private >*/ + FU_CONFIG_LOAD_FLAG_LAST +} FuConfigLoadFlags; + FuConfig *fu_config_new (void); gboolean fu_config_load (FuConfig *self, + FuConfigLoadFlags flags, + GError **error); +gboolean fu_config_modify_and_save (FuConfig *self, + const gchar *key, + const gchar *value, 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_approved_firmware (FuConfig *self); GPtrArray *fu_config_get_remotes (FuConfig *self); FwupdRemote *fu_config_get_remote_by_id (FuConfig *self, const gchar *remote_id); G_END_DECLS - -#endif /* __FU_CONFIG_H */ - diff -Nru fwupd-1.0.9/src/fu-debug.c fwupd-1.2.10/src/fu-debug.c --- fwupd-1.0.9/src/fu-debug.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-debug.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2010-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuDebug" + #include #include #include @@ -13,35 +14,47 @@ #include typedef struct { + GOptionGroup *group; gboolean verbose; gboolean console; gchar **plugin_verbose; + gchar **daemon_verbose; } FuDebug; 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); } -static void -fu_debug_ignore_cb (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) +static gboolean +fu_debug_filter_cb (FuDebug *self, const gchar *log_domain, GLogLevelFlags log_level) { - /* syslog */ - switch (log_level) { - case G_LOG_LEVEL_INFO: - case G_LOG_LEVEL_CRITICAL: - case G_LOG_LEVEL_ERROR: - case G_LOG_LEVEL_WARNING: - g_print ("%s\n", message); - break; - default: - break; + const gchar *domains = g_getenv ("FWUPD_VERBOSE"); + g_auto(GStrv) domains_str = NULL; + + /* include important things by default only */ + if (domains == NULL) { + if (log_level == G_LOG_LEVEL_INFO || + log_level == G_LOG_LEVEL_CRITICAL || + log_level == G_LOG_LEVEL_WARNING || + log_level == G_LOG_LEVEL_ERROR) { + return TRUE; + } + return FALSE; } + + /* everything */ + if (g_strcmp0 (domains, "*") == 0) + return TRUE; + + /* filter on domain */ + domains_str = g_strsplit (domains, ",", -1); + return g_strv_contains ((const gchar * const *) domains_str, log_domain); } static void @@ -55,6 +68,10 @@ g_autoptr(GDateTime) dt = g_date_time_new_now_utc (); g_autoptr(GString) domain = NULL; + /* should ignore */ + if (!fu_debug_filter_cb (self, log_domain, log_level)) + return; + /* time header */ tmp = g_strdup_printf ("%02i:%02i:%02i:%04i", g_date_time_get_hour (dt), @@ -62,21 +79,21 @@ g_date_time_get_second (dt), g_date_time_get_microsecond (dt) / 1000); - /* make these shorter */ - if (g_str_has_prefix (log_domain, "FuPlugin")) - log_domain += 8; + /* each file should have set this */ + if (log_domain == NULL) + log_domain = "FIXME"; /* pad out domain */ domain = g_string_new (log_domain); - for (gsize i = domain->len; i < 3; i++) + for (gsize i = domain->len; i < 20; i++) g_string_append (domain, " "); /* to file */ if (!self->console) { if (tmp != NULL) - g_print ("%s ", tmp); - g_print ("%s ", domain->str); - g_print ("%s\n", message); + g_printerr ("%s ", tmp); + g_printerr ("%s ", domain->str); + g_printerr ("%s\n", message); return; } @@ -87,16 +104,16 @@ case G_LOG_LEVEL_WARNING: /* critical in red */ if (tmp != NULL) - g_print ("%c[%dm%s ", 0x1B, 32, tmp); - g_print ("%s ", domain->str); - g_print ("%c[%dm%s\n%c[%dm", 0x1B, 31, message, 0x1B, 0); + g_printerr ("%c[%dm%s ", 0x1B, 32, tmp); + g_printerr ("%s ", domain->str); + g_printerr ("%c[%dm%s\n%c[%dm", 0x1B, 31, message, 0x1B, 0); break; default: /* debug in blue */ if (tmp != NULL) - g_print ("%c[%dm%s ", 0x1B, 32, tmp); - g_print ("%s ", domain->str); - g_print ("%c[%dm%s\n%c[%dm", 0x1B, 34, message, 0x1B, 0); + g_printerr ("%c[%dm%s ", 0x1B, 32, tmp); + g_printerr ("%s ", domain->str); + g_printerr ("%c[%dm%s\n%c[%dm", 0x1B, 34, message, 0x1B, 0); break; } } @@ -111,10 +128,13 @@ const GOptionEntry main_entries[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &self->verbose, /* TRANSLATORS: turn on all debugging */ - N_("Show debugging information for all files"), NULL }, + N_("Show debugging information for all domains"), NULL }, { "plugin-verbose", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &self->plugin_verbose, /* TRANSLATORS: this is for plugin development */ N_("Show plugin verbose information"), "PLUGIN-NAME" }, + { "daemon-verbose", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &self->daemon_verbose, + /* TRANSLATORS: this is for daemon development */ + N_("Show daemon verbose information for a particular domain"), "DOMAIN" }, { NULL} }; @@ -133,18 +153,18 @@ /* verbose? */ if (self->verbose) { - g_setenv ("FWUPD_VERBOSE", "1", TRUE); - g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | - G_LOG_LEVEL_CRITICAL); - g_log_set_default_handler (fu_debug_handler_cb, self); - } else { - /* hide all debugging */ - g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, - fu_debug_ignore_cb, self); + g_setenv ("FWUPD_VERBOSE", "*", TRUE); + } else if (self->daemon_verbose != NULL) { + g_autofree gchar *str = g_strjoinv (",", self->daemon_verbose); + g_setenv ("FWUPD_VERBOSE", str, TRUE); } + /* redirect all domains to be able to change FWUPD_VERBOSE at runtime */ + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_MASK); + g_log_set_default_handler (fu_debug_handler_cb, self); + /* are we on an actual TTY? */ - self->console = (isatty (fileno (stdout)) == 1); + self->console = (isatty (fileno (stderr)) == 1); g_debug ("Verbose debugging %s (on console %i)", self->verbose ? "enabled" : "disabled", self->console); @@ -162,19 +182,19 @@ 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 -Nru fwupd-1.0.9/src/fu-debug.h fwupd-1.2.10/src/fu-debug.h --- fwupd-1.0.9/src/fu-debug.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-debug.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2010-2011 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_DEBUG_H__ -#define __FU_DEBUG_H__ +#pragma once #include +G_BEGIN_DECLS + GOptionGroup *fu_debug_get_option_group (void); -#endif /* __FU_DEBUG_H__ */ +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-device.c fwupd-1.2.10/src/fu-device.c --- fwupd-1.0.9/src/fu-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,18 +1,24 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuDevice" + #include "config.h" #include -#include #include #include +#include "fu-common.h" +#include "fu-common-version.h" #include "fu-device-private.h" +#include "fu-mutex.h" + +#include "fwupd-common.h" +#include "fwupd-device-private.h" /** * SECTION:fu-device @@ -26,24 +32,35 @@ static void fu_device_finalize (GObject *object); typedef struct { + gchar *alternate_id; gchar *equivalent_id; FuDevice *alternate; FuDevice *parent; /* noref */ FuQuirks *quirks; GHashTable *metadata; + GRWLock metadata_mutex; GPtrArray *parent_guids; + GRWLock parent_guids_mutex; GPtrArray *children; guint remove_delay; /* ms */ FwupdStatus status; guint progress; guint order; + guint priority; + guint poll_id; + gboolean done_probe; + gboolean done_setup; + guint64 size_min; + guint64 size_max; + gint open_refcount; /* atomic */ } FuDevicePrivate; enum { PROP_0, PROP_STATUS, PROP_PROGRESS, - PROP_PLATFORM_ID, + PROP_PHYSICAL_ID, + PROP_LOGICAL_ID, PROP_QUIRKS, PROP_LAST }; @@ -55,8 +72,8 @@ fu_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - FuDevice *device = FU_DEVICE (object); - FuDevicePrivate *priv = GET_PRIVATE (device); + FuDevice *self = FU_DEVICE (object); + FuDevicePrivate *priv = GET_PRIVATE (self); switch (prop_id) { case PROP_STATUS: g_value_set_uint (value, priv->status); @@ -64,8 +81,11 @@ case PROP_PROGRESS: g_value_set_uint (value, priv->progress); break; - case PROP_PLATFORM_ID: - g_value_set_string (value, fu_device_get_platform_id (device)); + case PROP_PHYSICAL_ID: + g_value_set_string (value, fu_device_get_physical_id (self)); + break; + case PROP_LOGICAL_ID: + g_value_set_string (value, fu_device_get_logical_id (self)); break; case PROP_QUIRKS: g_value_set_object (value, priv->quirks); @@ -80,19 +100,22 @@ fu_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - FuDevice *device = FU_DEVICE (object); + FuDevice *self = FU_DEVICE (object); switch (prop_id) { case PROP_STATUS: - fu_device_set_status (device, g_value_get_uint (value)); + fu_device_set_status (self, g_value_get_uint (value)); break; case PROP_PROGRESS: - fu_device_set_progress (device, g_value_get_uint (value)); + fu_device_set_progress (self, g_value_get_uint (value)); break; - case PROP_PLATFORM_ID: - fu_device_set_platform_id (device, g_value_get_string (value)); + case PROP_PHYSICAL_ID: + fu_device_set_physical_id (self, g_value_get_string (value)); + break; + case PROP_LOGICAL_ID: + fu_device_set_logical_id (self, g_value_get_string (value)); break; case PROP_QUIRKS: - fu_device_set_quirks (device, g_value_get_object (value)); + fu_device_set_quirks (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -101,8 +124,82 @@ } /** + * fu_device_poll: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Polls a device, typically querying the hardware for status. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_poll (FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* subclassed */ + if (klass->poll != NULL) { + if (!klass->poll (self, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_device_poll_cb (gpointer user_data) +{ + FuDevice *self = FU_DEVICE (user_data); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error_local = NULL; + if (!fu_device_poll (self, &error_local)) { + g_warning ("disabling polling: %s", error_local->message); + priv->poll_id = 0; + return G_SOURCE_REMOVE; + } + return G_SOURCE_CONTINUE; +} + +/** + * fu_device_set_poll_interval: + * @self: a #FuPlugin + * @interval: duration in ms, or 0 to disable + * + * Polls the hardware every interval period. If the subclassed `->poll()` method + * returns %FALSE then a warning is printed to the console and the poll is + * disabled until the next call to fu_device_set_poll_interval(). + * + * Since: 1.1.2 + **/ +void +fu_device_set_poll_interval (FuDevice *self, guint interval) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + + g_return_if_fail (FU_IS_DEVICE (self)); + + if (priv->poll_id != 0) { + g_source_remove (priv->poll_id); + priv->poll_id = 0; + } + if (interval == 0) + return; + if (interval % 1000 == 0) { + priv->poll_id = g_timeout_add_seconds (interval / 1000, + fu_device_poll_cb, + self); + } else { + priv->poll_id = g_timeout_add (interval, fu_device_poll_cb, self); + } +} + +/** * fu_device_get_order: - * @device: a #FuPlugin + * @self: a #FuPlugin * * Gets the device order, where higher numbers are installed after lower * numbers. @@ -112,15 +209,16 @@ * Since: 1.0.8 **/ guint -fu_device_get_order (FuDevice *device) +fu_device_get_order (FuDevice *self) { - FuDevicePrivate *priv = fu_device_get_instance_private (device); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); return priv->order; } /** * fu_device_set_order: - * @device: a #FuDevice + * @self: a #FuDevice * @order: a integer value * * Sets the device order, where higher numbers are installed after lower @@ -129,69 +227,149 @@ * Since: 1.0.8 **/ void -fu_device_set_order (FuDevice *device, guint order) +fu_device_set_order (FuDevice *self, guint order) { - FuDevicePrivate *priv = fu_device_get_instance_private (device); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); priv->order = order; } +/** + * fu_device_get_priority: + * @self: a #FuPlugin + * + * Gets the device priority, where higher numbers are better. + * + * Returns: the integer value + * + * Since: 1.1.1 + **/ +guint +fu_device_get_priority (FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); + return priv->priority; +} + +/** + * fu_device_set_priority: + * @self: a #FuDevice + * @priority: a integer value + * + * Sets the device priority, where higher numbers are better. + * + * Since: 1.1.1 + **/ +void +fu_device_set_priority (FuDevice *self, guint priority) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); + priv->priority = priority; +} + const gchar * -fu_device_get_equivalent_id (FuDevice *device) +fu_device_get_equivalent_id (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); return priv->equivalent_id; } void -fu_device_set_equivalent_id (FuDevice *device, const gchar *equivalent_id) +fu_device_set_equivalent_id (FuDevice *self, const gchar *equivalent_id) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); g_free (priv->equivalent_id); priv->equivalent_id = g_strdup (equivalent_id); } /** * fu_device_get_alternate: - * @device: A #FuDevice + * @self: A #FuDevice + * + * Gets any alternate device ID. An alternate device may be linked to the primary + * device in some way. + * + * Returns: (transfer none): a #FuDevice or %NULL + * + * Since: 1.1.0 + **/ +const gchar * +fu_device_get_alternate_id (FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + return priv->alternate_id; +} + +/** + * fu_device_set_alternate: + * @self: A #FuDevice + * @alternate: Another #FuDevice + * + * Sets any alternate device ID. An alternate device may be linked to the primary + * device in some way. + * + * Since: 1.1.0 + **/ +void +fu_device_set_alternate_id (FuDevice *self, const gchar *alternate_id) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); + g_free (priv->alternate_id); + priv->alternate_id = g_strdup (alternate_id); +} + +/** + * fu_device_get_alternate: + * @self: A #FuDevice * * Gets any alternate device. An alternate device may be linked to the primary * device in some way. * + * The alternate object will be matched from the ID set in fu_device_set_alternate_id() + * and will be assigned by the daemon. This means if the ID is not found as an + * added device, then this function will return %NULL. + * * Returns: (transfer none): a #FuDevice or %NULL * * Since: 0.7.2 **/ FuDevice * -fu_device_get_alternate (FuDevice *device) +fu_device_get_alternate (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); return priv->alternate; } /** * fu_device_set_alternate: - * @device: A #FuDevice + * @self: A #FuDevice * @alternate: Another #FuDevice * * Sets any alternate device. An alternate device may be linked to the primary * device in some way. * + * This function is only usable by the daemon, not directly from plugins. + * * Since: 0.7.2 **/ void -fu_device_set_alternate (FuDevice *device, FuDevice *alternate) +fu_device_set_alternate (FuDevice *self, FuDevice *alternate) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); g_set_object (&priv->alternate, alternate); } /** * fu_device_get_parent: - * @device: A #FuDevice + * @self: A #FuDevice * * Gets any parent device. An parent device is logically "above" the current * device and this may be reflected in client tools. @@ -207,29 +385,31 @@ * Since: 1.0.8 **/ FuDevice * -fu_device_get_parent (FuDevice *device) +fu_device_get_parent (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); return priv->parent; } -static void -fu_device_set_parent (FuDevice *device, FuDevice *parent) +void +fu_device_set_parent (FuDevice *self, FuDevice *parent) { - FuDevicePrivate *priv = GET_PRIVATE (device); + FuDevicePrivate *priv = GET_PRIVATE (self); + + g_return_if_fail (FU_IS_DEVICE (self)); g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &priv->parent); priv->parent = parent; /* this is what goes over D-Bus */ - fwupd_device_set_parent_id (FWUPD_DEVICE (device), - device != NULL ? fu_device_get_id (parent) : NULL); + fwupd_device_set_parent_id (FWUPD_DEVICE (self), + parent != NULL ? fu_device_get_id (parent) : NULL); } /** * fu_device_get_children: - * @device: A #FuDevice + * @self: A #FuDevice * * Gets any child devices. A child device is logically "below" the current * device and this may be reflected in client tools. @@ -239,16 +419,16 @@ * Since: 1.0.8 **/ GPtrArray * -fu_device_get_children (FuDevice *device) +fu_device_get_children (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); return priv->children; } /** * fu_device_add_child: - * @device: A #FuDevice + * @self: A #FuDevice * @child: Another #FuDevice * * Sets any child device. An child device is logically linked to the primary @@ -257,10 +437,10 @@ * Since: 1.0.8 **/ void -fu_device_add_child (FuDevice *device, FuDevice *child) +fu_device_add_child (FuDevice *self, FuDevice *child) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); g_return_if_fail (FU_IS_DEVICE (child)); /* add if the child does not already exist */ @@ -272,13 +452,23 @@ g_ptr_array_add (priv->children, g_object_ref (child)); /* copy from main device if unset */ + if (fu_device_get_physical_id (child) == NULL && + fu_device_get_physical_id (self) != NULL) + fu_device_set_physical_id (child, fu_device_get_physical_id (self)); if (fu_device_get_vendor (child) == NULL) - fu_device_set_vendor (child, fu_device_get_vendor (device)); + fu_device_set_vendor (child, fu_device_get_vendor (self)); if (fu_device_get_vendor_id (child) == NULL) - fu_device_set_vendor_id (child, fu_device_get_vendor_id (device)); + fu_device_set_vendor_id (child, fu_device_get_vendor_id (self)); + if (fu_device_get_icons(child)->len == 0) { + GPtrArray *icons = fu_device_get_icons (self); + for (guint i = 0; i < icons->len; i++) { + const gchar *icon_name = g_ptr_array_index (icons, i); + fu_device_add_icon (child, icon_name); + } + } /* ensure the parent is also set on the child */ - fu_device_set_parent (child, device); + fu_device_set_parent (child, self); /* order devices so they are updated in the correct sequence */ if (fu_device_has_flag (child, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST)) { @@ -292,26 +482,28 @@ /** * fu_device_get_parent_guids: - * @device: A #FuDevice + * @self: A #FuDevice * * Gets any parent device GUIDs. If a device is added to the daemon that matches - * any GUIDs added from fu_device_add_parent_guid() then this device is marked the parent of @device. + * any GUIDs added from fu_device_add_parent_guid() then this device is marked the parent of @self. * * Returns: (transfer none) (element-type utf8): a list of GUIDs * * Since: 1.0.8 **/ GPtrArray * -fu_device_get_parent_guids (FuDevice *device) +fu_device_get_parent_guids (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->parent_guids_mutex); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + g_return_val_if_fail (locker != NULL, NULL); return priv->parent_guids; } /** * fu_device_has_parent_guid: - * @device: A #FuDevice + * @self: A #FuDevice * @guid: a GUID * * Searches the list of parent GUIDs for a string match. @@ -321,10 +513,12 @@ * Since: 1.0.8 **/ gboolean -fu_device_has_parent_guid (FuDevice *device, const gchar *guid) +fu_device_has_parent_guid (FuDevice *self, const gchar *guid) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->parent_guids_mutex); + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (locker != NULL, FALSE); for (guint i = 0; i < priv->parent_guids->len; i++) { const gchar *guid_tmp = g_ptr_array_index (priv->parent_guids, i); if (g_strcmp0 (guid_tmp, guid) == 0) @@ -335,28 +529,33 @@ /** * fu_device_add_parent_guid: - * @device: A #FuDevice + * @self: A #FuDevice * @guid: a GUID * * Sets any parent device using a GUID. An parent device is logically linked to - * the primary device in some way and can be added before or after @device. + * the primary device in some way and can be added before or after @self. * * The GUIDs are searched in order, and so the order of adding GUIDs may be * important if more than one parent device might match. * + * If the parent device is removed, any children logically linked to it will + * also be removed. + * * Since: 1.0.8 **/ void -fu_device_add_parent_guid (FuDevice *device, const gchar *guid) +fu_device_add_parent_guid (FuDevice *self, const gchar *guid) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = NULL; + + g_return_if_fail (FU_IS_DEVICE (self)); 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_device_has_parent_guid (device, tmp)) + if (!fwupd_guid_is_valid (guid)) { + g_autofree gchar *tmp = fwupd_guid_hash_string (guid); + if (fu_device_has_parent_guid (self, tmp)) return; g_debug ("using %s for %s", tmp, guid); g_ptr_array_add (priv->parent_guids, g_steal_pointer (&tmp)); @@ -364,39 +563,412 @@ } /* already valid */ - if (fu_device_has_parent_guid (device, guid)) + if (fu_device_has_parent_guid (self, guid)) return; + locker = g_rw_lock_writer_locker_new (&priv->parent_guids_mutex); + g_return_if_fail (locker != NULL); g_ptr_array_add (priv->parent_guids, g_strdup (guid)); } +static gboolean +fu_device_add_child_by_type_guid (FuDevice *self, + GType type, + const gchar *guid, + GError **error) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(FuDevice) child = NULL; + child = g_object_new (type, + "quirks", priv->quirks, + "logical-id", guid, + NULL); + fu_device_add_guid (child, guid); + if (fu_device_get_physical_id (self) != NULL) + fu_device_set_physical_id (child, fu_device_get_physical_id (self)); + if (!fu_device_ensure_id (self, error)) + return FALSE; + if (!fu_device_probe (child, error)) + return FALSE; + fu_device_convert_instance_ids (child); + fu_device_add_child (self, child); + return TRUE; +} + +static gboolean +fu_device_add_child_by_kv (FuDevice *self, const gchar *str, GError **error) +{ + g_auto(GStrv) split = g_strsplit (str, "|", -1); + + /* type same as parent */ + if (g_strv_length (split) == 1) { + return fu_device_add_child_by_type_guid (self, + G_OBJECT_TYPE (self), + split[1], + error); + } + + /* type specified */ + if (g_strv_length (split) == 2) { + GType devtype = g_type_from_name (split[0]); + if (devtype == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no GType registered"); + return FALSE; + } + return fu_device_add_child_by_type_guid (self, + devtype, + split[1], + error); + } + + /* more than one '|' */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "unable to add parse child section"); + return FALSE; +} + +static gboolean +fu_device_set_quirk_kv (FuDevice *self, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + if (g_strcmp0 (key, FU_QUIRKS_PLUGIN) == 0) { + fu_device_set_plugin (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_FLAGS) == 0) { + fu_device_set_custom_flags (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_NAME) == 0) { + fu_device_set_name (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_SUMMARY) == 0) { + fu_device_set_summary (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_VENDOR) == 0) { + fu_device_set_vendor (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_VENDOR_ID) == 0) { + fu_device_set_vendor_id (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_VERSION) == 0) { + fu_device_set_version (self, value, fu_device_get_version_format (self)); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_ICON) == 0) { + fu_device_add_icon (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_GUID) == 0) { + fu_device_add_guid (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_COUNTERPART_GUID) == 0) { + fu_device_add_counterpart_guid (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_PARENT_GUID) == 0) { + fu_device_add_parent_guid (self, value); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_FIRMWARE_SIZE_MIN) == 0) { + fu_device_set_firmware_size_min (self, fu_common_strtoull (value)); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_FIRMWARE_SIZE_MAX) == 0) { + fu_device_set_firmware_size_max (self, fu_common_strtoull (value)); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_FIRMWARE_SIZE) == 0) { + fu_device_set_firmware_size (self, fu_common_strtoull (value)); + return TRUE; + } + if (g_strcmp0 (key, FU_QUIRKS_INSTALL_DURATION) == 0) { + 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, fwupd_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++) { + if (!fu_device_add_child_by_kv (self, sections[i], error)) + return FALSE; + } + return TRUE; + } + + /* optional device-specific method */ + if (klass->set_quirk_kv != NULL) + return klass->set_quirk_kv (self, key, value, error); + + /* failed */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_device_add_guid_quirks (FuDevice *self, const gchar *guid) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + const gchar *key; + const gchar *value; + GHashTableIter iter; + + /* not set */ + if (priv->quirks == NULL) + return; + if (!fu_quirks_get_kvs_for_guid (priv->quirks, guid, &iter)) + return; + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + g_autoptr(GError) error = NULL; + if (!fu_device_set_quirk_kv (self, key, value, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { + g_warning ("failed to set quirk key %s=%s: %s", + key, value, error->message); + } + } + } +} + +/** + * fu_device_set_firmware_size: + * @self: A #FuDevice + * @size: Size in bytes + * + * Sets the exact allowed size of the firmware blob. + * + * Since: 1.2.6 + **/ +void +fu_device_set_firmware_size (FuDevice *self, guint64 size) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); + priv->size_min = size; + priv->size_max = size; +} + +/** + * fu_device_set_firmware_size_min: + * @self: A #FuDevice + * @size_min: Size in bytes + * + * Sets the minimum allowed size of the firmware blob. + * + * Since: 1.1.2 + **/ +void +fu_device_set_firmware_size_min (FuDevice *self, guint64 size_min) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); + priv->size_min = size_min; +} + +/** + * fu_device_set_firmware_size_max: + * @self: A #FuDevice + * @size_max: Size in bytes + * + * Sets the maximum allowed size of the firmware blob. + * + * Since: 1.1.2 + **/ +void +fu_device_set_firmware_size_max (FuDevice *self, guint64 size_max) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); + priv->size_max = size_max; +} + +/** + * fu_device_get_firmware_size_min: + * @self: A #FuDevice + * + * Gets the minimum size of the firmware blob. + * + * Returns: Size in bytes, or 0 if unset + * + * Since: 1.2.6 + **/ +guint64 +fu_device_get_firmware_size_min (FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); + return priv->size_min; +} + +/** + * fu_device_get_firmware_size_max: + * @self: A #FuDevice + * + * Gets the maximum size of the firmware blob. + * + * Returns: Size in bytes, or 0 if unset + * + * Since: 1.2.6 + **/ +guint64 +fu_device_get_firmware_size_max (FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); + return priv->size_max; +} + +static void +fu_device_add_guid_safe (FuDevice *self, const gchar *guid) +{ + /* add the device GUID before adding additional GUIDs from quirks + * to ensure the bootloader GUID is listed after the runtime GUID */ + fwupd_device_add_guid (FWUPD_DEVICE (self), 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) +{ + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (guid != NULL, FALSE); + + /* make valid */ + if (!fwupd_guid_is_valid (guid)) { + g_autofree gchar *tmp = fwupd_guid_hash_string (guid); + return fwupd_device_has_guid (FWUPD_DEVICE (self), tmp); + } + + /* already valid */ + return fwupd_device_has_guid (FWUPD_DEVICE (self), guid); +} + +/* private */ +void +fu_device_add_instance_id_full (FuDevice *self, + const gchar *instance_id, + FuDeviceInstanceFlags flags) +{ + g_autofree gchar *guid = NULL; + if (fwupd_guid_is_valid (instance_id)) { + g_warning ("use fu_device_add_guid(\"%s\") instead!", instance_id); + fu_device_add_guid_safe (self, instance_id); + return; + } + + /* it seems odd adding the instance ID and the GUID quirks and not just + * calling fu_device_add_guid_safe() -- but we want the quirks to match + * so the plugin is set, but not the LVFS metadata to match firmware + * until we're sure the device isn't using _NO_AUTO_INSTANCE_IDS */ + guid = fwupd_guid_hash_string (instance_id); + fu_device_add_guid_quirks (self, guid); + if ((flags & FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS) == 0) + fwupd_device_add_instance_id (FWUPD_DEVICE (self), instance_id); +} + +/** + * fu_device_add_instance_id: + * @self: A #FuDevice + * @instance_id: the InstanceID, e.g. `PCI\VEN_10EC&DEV_525A` + * + * Adds an instance ID to the device. If the @instance_id argument is already a + * valid GUID then fu_device_add_guid() should be used instead. + * + * Since: 1.2.5 + **/ +void +fu_device_add_instance_id (FuDevice *self, const gchar *instance_id) +{ + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (instance_id != NULL); + fu_device_add_instance_id_full (self, instance_id, FU_DEVICE_INSTANCE_FLAG_NONE); +} + /** * fu_device_add_guid: - * @device: A #FuDevice + * @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 fwupd_guid_hash_string(). * * Since: 0.7.2 **/ void -fu_device_add_guid (FuDevice *device, const gchar *guid) +fu_device_add_guid (FuDevice *self, const gchar *guid) +{ + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (guid != NULL); + if (!fwupd_guid_is_valid (guid)) { + fu_device_add_instance_id (self, guid); + return; + } + fu_device_add_guid_safe (self, guid); +} + +/** + * fu_device_add_counterpart_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 fwupd_guid_hash_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 + * type of GUID does not cause a "cascade" by matching using the quirk database. + * + * Since: 1.1.2 + **/ +void +fu_device_add_counterpart_guid (FuDevice *self, const gchar *guid) { + g_return_if_fail (FU_IS_DEVICE (self)); + 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); - g_debug ("using %s for %s", tmp, guid); - fwupd_device_add_guid (FWUPD_DEVICE (device), tmp); + if (!fwupd_guid_is_valid (guid)) { + g_autofree gchar *tmp = fwupd_guid_hash_string (guid); + fwupd_device_add_guid (FWUPD_DEVICE (self), tmp); return; } /* already valid */ - fwupd_device_add_guid (FWUPD_DEVICE (device), guid); + fwupd_device_add_guid (FWUPD_DEVICE (self), guid); } /** * fu_device_get_guids_as_str: - * @device: A #FuDevice + * @self: A #FuDevice * * Gets the device GUIDs as a joined string, which may be useful for error * messages. @@ -406,10 +978,15 @@ * Since: 1.0.8 **/ gchar * -fu_device_get_guids_as_str (FuDevice *device) +fu_device_get_guids_as_str (FuDevice *self) { - GPtrArray *guids = fu_device_get_guids (device); - g_autofree gchar **tmp = g_new0 (gchar *, guids->len + 1); + GPtrArray *guids; + g_autofree gchar **tmp = NULL; + + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + + guids = fu_device_get_guids (self); + tmp = g_new0 (gchar *, guids->len + 1); for (guint i = 0; i < guids->len; i++) tmp[i] = g_ptr_array_index (guids, i); return g_strjoinv (",", tmp); @@ -417,7 +994,7 @@ /** * fu_device_get_metadata: - * @device: A #FuDevice + * @self: A #FuDevice * @key: the key * * Gets an item of metadata from the device. @@ -427,17 +1004,19 @@ * Since: 0.1.0 **/ const gchar * -fu_device_get_metadata (FuDevice *device, const gchar *key) +fu_device_get_metadata (FuDevice *self, const gchar *key) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->metadata_mutex); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (locker != NULL, NULL); return g_hash_table_lookup (priv->metadata, key); } /** * fu_device_get_metadata_boolean: - * @device: A #FuDevice + * @self: A #FuDevice * @key: the key * * Gets an item of metadata from the device. @@ -447,12 +1026,16 @@ * Since: 0.9.7 **/ gboolean -fu_device_get_metadata_boolean (FuDevice *device, const gchar *key) +fu_device_get_metadata_boolean (FuDevice *self, const gchar *key) { - FuDevicePrivate *priv = GET_PRIVATE (device); + FuDevicePrivate *priv = GET_PRIVATE (self); const gchar *tmp; - g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->metadata_mutex); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (locker != NULL, FALSE); + tmp = g_hash_table_lookup (priv->metadata, key); if (tmp == NULL) return FALSE; @@ -461,7 +1044,7 @@ /** * fu_device_get_metadata_integer: - * @device: A #FuDevice + * @self: A #FuDevice * @key: the key * * Gets an item of metadata from the device. @@ -471,15 +1054,17 @@ * Since: 0.9.7 **/ guint -fu_device_get_metadata_integer (FuDevice *device, const gchar *key) +fu_device_get_metadata_integer (FuDevice *self, const gchar *key) { - FuDevicePrivate *priv = GET_PRIVATE (device); + FuDevicePrivate *priv = GET_PRIVATE (self); const gchar *tmp; gchar *endptr = NULL; guint64 val; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->metadata_mutex); - g_return_val_if_fail (FU_IS_DEVICE (device), G_MAXUINT); + g_return_val_if_fail (FU_IS_DEVICE (self), G_MAXUINT); g_return_val_if_fail (key != NULL, G_MAXUINT); + g_return_val_if_fail (locker != NULL, G_MAXUINT); tmp = g_hash_table_lookup (priv->metadata, key); if (tmp == NULL) @@ -494,7 +1079,7 @@ /** * fu_device_set_metadata: - * @device: A #FuDevice + * @self: A #FuDevice * @key: the key * @value: the string value * @@ -503,18 +1088,20 @@ * Since: 0.1.0 **/ void -fu_device_set_metadata (FuDevice *device, const gchar *key, const gchar *value) +fu_device_set_metadata (FuDevice *self, const gchar *key, const gchar *value) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_writer_locker_new (&priv->metadata_mutex); + g_return_if_fail (FU_IS_DEVICE (self)); g_return_if_fail (key != NULL); g_return_if_fail (value != NULL); + g_return_if_fail (locker != NULL); g_hash_table_insert (priv->metadata, g_strdup (key), g_strdup (value)); } /** * fu_device_set_metadata_boolean: - * @device: A #FuDevice + * @self: A #FuDevice * @key: the key * @value: the boolean value * @@ -524,14 +1111,17 @@ * Since: 0.9.7 **/ void -fu_device_set_metadata_boolean (FuDevice *device, const gchar *key, gboolean value) +fu_device_set_metadata_boolean (FuDevice *self, const gchar *key, gboolean value) { - fu_device_set_metadata (device, key, value ? "true" : "false"); + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (key != NULL); + + fu_device_set_metadata (self, key, value ? "true" : "false"); } /** * fu_device_set_metadata_integer: - * @device: A #FuDevice + * @self: A #FuDevice * @key: the key * @value: the unsigned integer value * @@ -541,15 +1131,19 @@ * Since: 0.9.7 **/ void -fu_device_set_metadata_integer (FuDevice *device, const gchar *key, guint value) +fu_device_set_metadata_integer (FuDevice *self, const gchar *key, guint value) { g_autofree gchar *tmp = g_strdup_printf ("%u", value); - fu_device_set_metadata (device, key, tmp); + + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (key != NULL); + + fu_device_set_metadata (self, key, tmp); } /** * fu_device_set_name: - * @device: A #FuDevice + * @self: A #FuDevice * @value: a device name * * Sets the name on the device. Any invalid parts will be converted or removed. @@ -557,33 +1151,38 @@ * Since: 0.7.1 **/ void -fu_device_set_name (FuDevice *device, const gchar *value) +fu_device_set_name (FuDevice *self, const gchar *value) { g_autoptr(GString) new = g_string_new (value); + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (value != NULL); + /* overwriting? */ - if (g_strcmp0 (value, fu_device_get_name (device)) == 0) { - g_warning ("device %s overwriting same name value: %s", - fu_device_get_id (device), value); + if (g_strcmp0 (value, fu_device_get_name (self)) == 0) { + const gchar *id = fu_device_get_id (self); + g_debug ("%s device overwriting same name value: %s", + id != NULL ? id : "unknown", value); return; } /* changing */ - if (fu_device_get_name (device) != NULL) { - g_debug ("device %s overwriting name value: %s->%s", - fu_device_get_id (device), - fu_device_get_name (device), + if (fu_device_get_name (self) != NULL) { + const gchar *id = fu_device_get_id (self); + g_debug ("%s device overwriting name value: %s->%s", + id != NULL ? id : "unknown", + fu_device_get_name (self), value); } g_strdelimit (new->str, "_", ' '); - as_utils_string_replace (new, "(TM)", "™"); - fwupd_device_set_name (FWUPD_DEVICE (device), new->str); + fu_common_string_replace (new, "(TM)", "™"); + fwupd_device_set_name (FWUPD_DEVICE (self), new->str); } /** * fu_device_set_id: - * @device: A #FuDevice + * @self: A #FuDevice * @id: a string, e.g. `tbt-port1` * * Sets the ID on the device. The ID should represent the *connection* of the @@ -597,127 +1196,281 @@ * Since: 0.7.1 **/ void -fu_device_set_id (FuDevice *device, const gchar *id) +fu_device_set_id (FuDevice *self, const gchar *id) { + FuDevicePrivate *priv = GET_PRIVATE (self); g_autofree gchar *id_hash = NULL; - g_return_if_fail (FU_IS_DEVICE (device)); + + g_return_if_fail (FU_IS_DEVICE (self)); g_return_if_fail (id != NULL); + id_hash = g_compute_checksum_for_string (G_CHECKSUM_SHA1, id, -1); g_debug ("using %s for %s", id_hash, id); - fwupd_device_set_id (FWUPD_DEVICE (device), id_hash); + fwupd_device_set_id (FWUPD_DEVICE (self), id_hash); + + /* ensure the parent ID is set */ + for (guint i = 0; i < priv->children->len; i++) { + FuDevice *devtmp = g_ptr_array_index (priv->children, i); + fwupd_device_set_parent_id (FWUPD_DEVICE (devtmp), id_hash); + } } /** - * fu_device_set_serial: - * @device: A #FuDevice - * @serial: a serial number string, e.g. `0000123` + * fu_device_set_version: + * @self: A #FuDevice + * @version: a string, e.g. `1.2.3` + * @fmt: a #FwupdVersionFormat, e.g. %FWUPD_VERSION_FORMAT_TRIPLET * - * Sets the serial number for the device. + * Sets the device version, sanitizing the string if required. * - * Since: 1.0.3 + * Since: 1.2.9 **/ void -fu_device_set_serial (FuDevice *device, const gchar *serial) +fu_device_set_version (FuDevice *self, const gchar *version, FwupdVersionFormat fmt) +{ + g_autofree gchar *version_safe = NULL; + g_autoptr(GError) error = NULL; + + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (version != NULL); + + /* sanitize if required */ + if (fu_device_has_flag (self, FWUPD_DEVICE_FLAG_ENSURE_SEMVER)) { + version_safe = fu_common_version_ensure_semver (version); + if (g_strcmp0 (version, version_safe) != 0) + g_debug ("converted '%s' to '%s'", version, version_safe); + } else { + version_safe = g_strdup (version); + } + + /* print a console warning for an invalid version, if semver */ + if (!fu_common_version_verify_format (version_safe, fmt, &error)) + g_warning ("%s", error->message); + + fu_device_set_version_format (self, fmt); + fwupd_device_set_version (FWUPD_DEVICE (self), version_safe); +} + +/** + * fu_device_ensure_id: + * @self: A #FuDevice + * @error: A #GError + * + * If not already set, generates a device ID with the optional physical and + * logical IDs. + * + * Returns: %TRUE on success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_ensure_id (FuDevice *self, GError **error) { - g_return_if_fail (FU_IS_DEVICE (device)); - g_return_if_fail (serial != NULL); - fu_device_set_metadata (device, "serial", serial); + g_autofree gchar *device_id = NULL; + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* already set */ + if (fu_device_get_id (self) != NULL) + return TRUE; + + /* nothing we can do! */ + if (fu_device_get_physical_id (self) == NULL) { + g_autofree gchar *tmp = fu_device_to_string (self); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot ensure ID: %s", tmp); + return FALSE; + } + + /* logical may be NULL */ + device_id = g_strjoin (":", + fu_device_get_physical_id (self), + fu_device_get_logical_id (self), + NULL); + fu_device_set_id (self, device_id); + return TRUE; } /** - * fu_device_get_serial: - * @device: A #FuDevice + * fu_device_get_logical_id: + * @self: A #FuDevice * - * Gets the serial number for the device. + * Gets the logical ID set for the device, which disambiguates devices with the + * same physical ID. * * Returns: a string value, or %NULL if never set. * - * Since: 1.0.3 + * Since: 1.1.2 **/ const gchar * -fu_device_get_serial (FuDevice *device) +fu_device_get_logical_id (FuDevice *self) { - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); - return fu_device_get_metadata (device, "serial"); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + return fu_device_get_metadata (self, "logical-id"); } /** - * fu_device_set_plugin_hints: - * @device: A #FuDevice - * @plugin_hints: a string + * fu_device_set_logical_id: + * @self: A #FuDevice + * @logical_id: a string, e.g. `dev2` * - * Sets the hint the the plugin from the quirk system that can be used to - * do affect device matching. The actual string format is defined by the plugin. + * Sets the logical ID on the device. This is designed to disambiguate devices + * with the same physical ID. * - * Since: 1.0.3 + * Since: 1.1.2 **/ void -fu_device_set_plugin_hints (FuDevice *device, const gchar *plugin_hints) +fu_device_set_logical_id (FuDevice *self, const gchar *logical_id) { - g_return_if_fail (FU_IS_DEVICE (device)); - g_return_if_fail (plugin_hints != NULL); - fu_device_set_metadata (device, "PluginHints", plugin_hints); + g_return_if_fail (FU_IS_DEVICE (self)); + fu_device_set_metadata (self, "logical-id", logical_id); } /** - * fu_device_get_plugin_hints: - * @device: A #FuDevice + * fu_device_set_physical_id: + * @self: A #FuDevice + * @physical_id: a string that identifies the physical device connection + * + * Sets the physical ID on the device which represents the electrical connection + * of the device to the system. Multiple #FuDevices can share a physical ID. + * + * The physical ID is used to remove logical devices when a physical device has + * been removed from the system. + * + * A sysfs or devpath is not a physical ID, but could be something like + * `PCI_SLOT_NAME=0000:3e:00.0`. + * + * Since: 1.1.2 + **/ +void +fu_device_set_physical_id (FuDevice *self, const gchar *physical_id) +{ + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (physical_id != NULL); + fu_device_set_metadata (self, "physical-id", physical_id); +} + +/** + * fu_device_get_physical_id: + * @self: A #FuDevice + * + * Gets the physical ID set for the device, which represents the electrical + * connection used to compare devices. * - * Gets the plugin hint for the device from the quirk system. + * Multiple #FuDevices can share a single physical ID. * * Returns: a string value, or %NULL if never set. * - * Since: 1.0.3 + * Since: 1.1.2 **/ const gchar * -fu_device_get_plugin_hints (FuDevice *device) +fu_device_get_physical_id (FuDevice *self) +{ + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + return fu_device_get_metadata (self, "physical-id"); +} + +static void +fu_device_set_custom_flag (FuDevice *self, const gchar *hint) { - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); - return fu_device_get_metadata (device, "PluginHints"); + FwupdDeviceFlags flag; + + /* is this a known device flag */ + flag = fwupd_device_flag_from_string (hint); + if (flag == FWUPD_DEVICE_FLAG_UNKNOWN) + return; + + /* being both a bootloader and requiring a bootloader is invalid */ + if (flag == FWUPD_DEVICE_FLAG_NONE || + flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) { + fu_device_remove_flag (self, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + if (flag == FWUPD_DEVICE_FLAG_NONE || + flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) { + fu_device_remove_flag (self, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); + } + + /* none is not used as an "exported" flag */ + if (flag != FWUPD_DEVICE_FLAG_NONE) + fu_device_add_flag (self, flag); } /** - * fu_device_set_platform_id: - * @device: A #FuDevice - * @platform_id: a platform string, e.g. `/sys/devices/usb1/1-1/1-1.2` + * fu_device_set_custom_flags: + * @self: A #FuDevice + * @custom_flags: a string * - * Sets the Platform ID on the device. If unset, the ID will automatically - * be set using a hash of the @platform_id value. + * Sets the custom flags from the quirk system that can be used to + * affect device matching. The actual string format is defined by the plugin. * - * Since: 1.0.2 + * Since: 1.1.0 **/ void -fu_device_set_platform_id (FuDevice *device, const gchar *platform_id) +fu_device_set_custom_flags (FuDevice *self, const gchar *custom_flags) { - g_return_if_fail (FU_IS_DEVICE (device)); - g_return_if_fail (platform_id != NULL); + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (custom_flags != NULL); + + /* display what was set when converting to a string */ + fu_device_set_metadata (self, "CustomFlags", custom_flags); - /* automatically use this */ - if (fu_device_get_id (device) == NULL) { - g_autofree gchar *id_guid = NULL; - id_guid = g_strdup_printf ("%s:%s", platform_id, - fu_device_get_guid_default (device)); - fu_device_set_id (device, id_guid); + /* look for any standard FwupdDeviceFlags */ + if (custom_flags != NULL) { + g_auto(GStrv) hints = g_strsplit (custom_flags, ",", -1); + for (guint i = 0; hints[i] != NULL; i++) + fu_device_set_custom_flag (self, hints[i]); } - fu_device_set_metadata (device, "platform-id", platform_id); } /** - * fu_device_get_platform_id: - * @device: A #FuDevice + * fu_device_get_custom_flags: + * @self: A #FuDevice * - * Gets the Platform ID set for the device, which represents the connection - * string used to compare devices. + * Gets the custom flags for the device from the quirk system. * * Returns: a string value, or %NULL if never set. * - * Since: 1.0.2 + * Since: 1.1.0 **/ const gchar * -fu_device_get_platform_id (FuDevice *device) +fu_device_get_custom_flags (FuDevice *self) +{ + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + return fu_device_get_metadata (self, "CustomFlags"); +} + +/** + * fu_device_has_custom_flag: + * @self: A #FuDevice + * @hint: A string, e.g. "bootloader" + * + * Checks if the custom flag exists for the device from the quirk system. + * + * It may be more efficient to call fu_device_get_custom_flags() and split the + * string locally if checking for lots of different flags. + * + * Returns: %TRUE if the hint exists + * + * Since: 1.1.0 + **/ +gboolean +fu_device_has_custom_flag (FuDevice *self, const gchar *hint) { - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); - return fu_device_get_metadata (device, "platform-id"); + const gchar *hint_str; + g_auto(GStrv) hints = NULL; + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (hint != NULL, FALSE); + + /* no hint is perfectly valid */ + hint_str = fu_device_get_custom_flags (self); + if (hint_str == NULL) + return FALSE; + hints = g_strsplit (hint_str, ",", -1); + return g_strv_contains ((const gchar * const *) hints, hint); } static void @@ -734,7 +1487,7 @@ /** * fu_device_get_remove_delay: - * @device: A #FuDevice + * @self: A #FuDevice * * Returns the maximum delay expected when replugging the device going into * bootloader mode. @@ -744,16 +1497,16 @@ * Since: 1.0.2 **/ guint -fu_device_get_remove_delay (FuDevice *device) +fu_device_get_remove_delay (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), 0); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); return priv->remove_delay; } /** * fu_device_set_remove_delay: - * @device: A #FuDevice + * @self: A #FuDevice * @remove_delay: the remove_delay value * * Sets the amount of time a device is allowed to return in bootloader mode. @@ -766,16 +1519,16 @@ * Since: 1.0.2 **/ void -fu_device_set_remove_delay (FuDevice *device, guint remove_delay) +fu_device_set_remove_delay (FuDevice *self, guint remove_delay) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); priv->remove_delay = remove_delay; } /** * fu_device_get_status: - * @device: A #FuDevice + * @self: A #FuDevice * * Returns what the device is currently doing. * @@ -784,16 +1537,16 @@ * Since: 1.0.3 **/ FwupdStatus -fu_device_get_status (FuDevice *device) +fu_device_get_status (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), 0); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); return priv->status; } /** * fu_device_set_status: - * @device: A #FuDevice + * @self: A #FuDevice * @status: the status value, e.g. %FWUPD_STATUS_DEVICE_WRITE * * Sets what the device is currently doing. @@ -801,19 +1554,19 @@ * Since: 1.0.3 **/ void -fu_device_set_status (FuDevice *device, FwupdStatus status) +fu_device_set_status (FuDevice *self, FwupdStatus status) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); if (priv->status == status) return; priv->status = status; - g_object_notify (G_OBJECT (device), "status"); + g_object_notify (G_OBJECT (self), "status"); } /** * fu_device_get_progress: - * @device: A #FuDevice + * @self: A #FuDevice * * Returns the progress completion. * @@ -822,16 +1575,16 @@ * Since: 1.0.3 **/ guint -fu_device_get_progress (FuDevice *device) +fu_device_get_progress (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), 0); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), 0); return priv->progress; } /** * fu_device_set_progress: - * @device: A #FuDevice + * @self: A #FuDevice * @progress: the progress percentage value * * Sets the progress completion. @@ -839,19 +1592,19 @@ * Since: 1.0.3 **/ void -fu_device_set_progress (FuDevice *device, guint progress) +fu_device_set_progress (FuDevice *self, guint progress) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); if (priv->progress == progress) return; priv->progress = progress; - g_object_notify (G_OBJECT (device), "progress"); + g_object_notify (G_OBJECT (self), "progress"); } /** * fu_device_set_progress_full: - * @device: A #FuDevice + * @self: A #FuDevice * @progress_done: the bytes already done * @progress_total: the total number of bytes * @@ -860,17 +1613,18 @@ * Since: 1.0.3 **/ void -fu_device_set_progress_full (FuDevice *device, gsize progress_done, gsize progress_total) +fu_device_set_progress_full (FuDevice *self, gsize progress_done, gsize progress_total) { gdouble percentage = 0.f; + g_return_if_fail (FU_IS_DEVICE (self)); if (progress_total > 0) percentage = (100.f * (gdouble) progress_done) / (gdouble) progress_total; - fu_device_set_progress (device, (guint) percentage); + fu_device_set_progress (self, (guint) percentage); } /** * fu_device_to_string: - * @device: A #FuDevice + * @self: A #FuDevice * * This allows us to easily print the FwupdDevice, the FwupdRelease and the * daemon-specific metadata. @@ -880,21 +1634,33 @@ * Since: 0.9.8 **/ gchar * -fu_device_to_string (FuDevice *device) +fu_device_to_string (FuDevice *self) { - FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); - FuDevicePrivate *priv = GET_PRIVATE (device); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + FuDevicePrivate *priv = GET_PRIVATE (self); GString *str = g_string_new (""); g_autofree gchar *tmp = NULL; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->metadata_mutex); g_autoptr(GList) keys = NULL; - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + g_return_val_if_fail (locker != NULL, NULL); - tmp = fwupd_device_to_string (FWUPD_DEVICE (device)); + tmp = fwupd_device_to_string (FWUPD_DEVICE (self)); if (tmp != NULL && tmp[0] != '\0') g_string_append (str, tmp); + if (priv->alternate_id != NULL) + fwupd_pad_kv_str (str, "AlternateId", priv->alternate_id); if (priv->equivalent_id != NULL) fwupd_pad_kv_str (str, "EquivalentId", priv->equivalent_id); + if (priv->size_min > 0) { + g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_min); + fwupd_pad_kv_str (str, "FirmwareSizeMin", sz); + } + if (priv->size_max > 0) { + g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_max); + fwupd_pad_kv_str (str, "FirmwareSizeMax", sz); + } keys = g_hash_table_get_keys (priv->metadata); for (GList *l = keys; l != NULL; l = l->next) { const gchar *key = l->data; @@ -904,14 +1670,14 @@ /* subclassed */ if (klass->to_string != NULL) - klass->to_string (device, str); + klass->to_string (self, str); return g_string_free (str, FALSE); } /** * fu_device_set_quirks: - * @device: A #FuDevice + * @self: A #FuDevice * @quirks: A #FuQuirks, or %NULL * * Sets the optional quirk information which may be useful to this device. @@ -921,17 +1687,17 @@ * Since: 1.0.3 **/ void -fu_device_set_quirks (FuDevice *device, FuQuirks *quirks) +fu_device_set_quirks (FuDevice *self, FuQuirks *quirks) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_if_fail (FU_IS_DEVICE (device)); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); if (g_set_object (&priv->quirks, quirks)) - g_object_notify (G_OBJECT (device), "quirks"); + g_object_notify (G_OBJECT (self), "quirks"); } /** * fu_device_get_quirks: - * @device: A #FuDevice + * @self: A #FuDevice * * Gets the quirk information which may be useful to this device. * @@ -940,16 +1706,16 @@ * Since: 1.0.3 **/ FuQuirks * -fu_device_get_quirks (FuDevice *device) +fu_device_get_quirks (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); return priv->quirks; } /** * fu_device_get_release_default: - * @device: A #FuDevice + * @self: A #FuDevice * * Gets the default release for the device, creating one if not found. * @@ -958,20 +1724,22 @@ * Since: 1.0.5 **/ FwupdRelease * -fu_device_get_release_default (FuDevice *device) +fu_device_get_release_default (FuDevice *self) { g_autoptr(FwupdRelease) rel = NULL; - if (fwupd_device_get_release_default (FWUPD_DEVICE (device)) != NULL) - return fwupd_device_get_release_default (FWUPD_DEVICE (device)); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + if (fwupd_device_get_release_default (FWUPD_DEVICE (self)) != NULL) + return fwupd_device_get_release_default (FWUPD_DEVICE (self)); rel = fwupd_release_new (); - fwupd_device_add_release (FWUPD_DEVICE (device), rel); + fwupd_device_add_release (FWUPD_DEVICE (self), rel); return rel; } /** * fu_device_write_firmware: - * @device: A #FuDevice + * @self: A #FuDevice * @fw: A #GBytes + * @flags: #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE * @error: A #GError * * Writes firmware to the device by calling a plugin-specific vfunc. @@ -981,11 +1749,15 @@ * Since: 1.0.8 **/ gboolean -fu_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +fu_device_write_firmware (FuDevice *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) { - FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + g_autoptr(GBytes) fw_new = NULL; - g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* no plugin-specific method */ @@ -997,13 +1769,87 @@ return FALSE; } + /* prepare (e.g. decompress) firmware */ + fw_new = fu_device_prepare_firmware (self, fw, flags, error); + if (fw_new == NULL) + return FALSE; + /* call vfunc */ - return klass->write_firmware (device, fw, error); + return klass->write_firmware (self, fw_new, flags, error); +} + +/** + * fu_device_prepare_firmware: + * @self: A #FuDevice + * @fw: A #GBytes + * @flags: #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: A #GError + * + * Prepares the firmware by calling an optional device-specific vfunc for the + * device, which can do things like decompressing or parsing of the firmware + * data. + * + * For all firmware, this checks the size of the firmware if limits have been + * set using fu_device_set_firmware_size_min(), fu_device_set_firmware_size_max() + * or using a quirk entry. + * + * Returns: (transfer full): A new #GBytes, or %NULL for error + * + * Since: 1.1.2 + **/ +GBytes * +fu_device_prepare_firmware (FuDevice *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + FuDevicePrivate *priv = GET_PRIVATE (self); + guint64 fw_sz; + g_autoptr(GBytes) fw_new = NULL; + + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + g_return_val_if_fail (fw != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* optionally subclassed */ + if (klass->prepare_firmware != NULL) { + fw_new = klass->prepare_firmware (self, fw, flags, error); + if (fw_new == NULL) + return NULL; + } else { + fw_new = g_bytes_ref (fw); + } + + /* check size */ + fw_sz = (guint64) g_bytes_get_size (fw_new); + if (priv->size_max > 0 && fw_sz > priv->size_max) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is %04x bytes larger than the allowed " + "maximum size of %04x bytes", + (guint) (fw_sz - priv->size_max), + (guint) priv->size_max); + return NULL; + } + if (priv->size_min > 0 && fw_sz < priv->size_min) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is %04x bytes smaller than the allowed " + "minimum size of %04x bytes", + (guint) (priv->size_min - fw_sz), + (guint) priv->size_max); + return NULL; + } + + return g_steal_pointer (&fw_new); } /** * fu_device_read_firmware: - * @device: A #FuDevice + * @self: A #FuDevice * @error: A #GError * * Reads firmware from the device by calling a plugin-specific vfunc. @@ -1013,11 +1859,11 @@ * Since: 1.0.8 **/ GBytes * -fu_device_read_firmware (FuDevice *device, GError **error) +fu_device_read_firmware (FuDevice *self, GError **error) { - FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); - g_return_val_if_fail (FU_IS_DEVICE (device), NULL); + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* no plugin-specific method */ @@ -1030,12 +1876,12 @@ } /* call vfunc */ - return klass->read_firmware (device, error); + return klass->read_firmware (self, error); } /** * fu_device_detach: - * @device: A #FuDevice + * @self: A #FuDevice * @error: A #GError * * Detaches a device from the application into bootloader mode. @@ -1045,11 +1891,11 @@ * Since: 1.0.8 **/ gboolean -fu_device_detach (FuDevice *device, GError **error) +fu_device_detach (FuDevice *self, GError **error) { - FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); - g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* no plugin-specific method */ @@ -1062,12 +1908,12 @@ } /* call vfunc */ - return klass->detach (device, error); + return klass->detach (self, error); } /** * fu_device_attach: - * @device: A #FuDevice + * @self: A #FuDevice * @error: A #GError * * Attaches a device from the bootloader into application mode. @@ -1077,11 +1923,11 @@ * Since: 1.0.8 **/ gboolean -fu_device_attach (FuDevice *device, GError **error) +fu_device_attach (FuDevice *self, GError **error) { - FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); - g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* no plugin-specific method */ @@ -1094,7 +1940,344 @@ } /* call vfunc */ - return klass->attach (device, error); + return klass->attach (self, error); +} + +/** + * fu_device_open: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Opens a device, optionally running a object-specific vfunc. + * + * Plugins can call fu_device_open() multiple times without calling + * fu_device_close(), but only the first call will actually invoke the vfunc. + * + * It is expected that plugins issue the same number of fu_device_open() and + * fu_device_close() methods when using a specific @self. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_open (FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + FuDevicePrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* already open */ + g_atomic_int_inc (&priv->open_refcount); + if (priv->open_refcount > 1) + return TRUE; + + /* probe */ + if (!fu_device_probe (self, error)) + return FALSE; + + /* ensure the device ID is already setup */ + if (!fu_device_ensure_id (self, error)) + return FALSE; + + /* subclassed */ + if (klass->open != NULL) { + if (!klass->open (self, error)) + return FALSE; + } + + /* setup */ + if (!fu_device_setup (self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +/** + * fu_device_close: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Closes a device, optionally running a object-specific vfunc. + * + * Plugins can call fu_device_close() multiple times without calling + * fu_device_open(), but only the last call will actually invoke the vfunc. + * + * It is expected that plugins issue the same number of fu_device_open() and + * fu_device_close() methods when using a specific @self. + * + * An error is returned if this method is called without having used the + * fu_device_open() method beforehand. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_close (FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + FuDevicePrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* not yet open */ + if (priv->open_refcount == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot close device, refcount already zero"); + return FALSE; + } + if (!g_atomic_int_dec_and_test (&priv->open_refcount)) + return TRUE; + + /* subclassed */ + if (klass->close != NULL) { + if (!klass->close (self, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_device_probe: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Probes a device, setting parameters on the object that does not need + * the device open or the interface claimed. + * If the device is not compatible then an error should be returned. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_probe (FuDevice *self, GError **error) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* already done */ + if (priv->done_probe) + return TRUE; + + /* subclassed */ + if (klass->probe != NULL) { + if (!klass->probe (self, error)) + return FALSE; + } + priv->done_probe = TRUE; + return TRUE; +} + +/** + * fu_device_convert_instance_ids: + * @self: A #FuDevice + * + * Converts all the Device Instance IDs added using fu_device_add_instance_id() + * into actual GUIDs, **unless** %FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS has + * been set. + * + * Plugins will only need to need to call this manually when adding child + * devices, as fu_device_setup() automatically calls this after the + * fu_device_probe() and fu_device_setup() virtual functions have been run. + * + * Since: 1.2.5 + **/ +void +fu_device_convert_instance_ids (FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + GPtrArray *instance_ids = fwupd_device_get_instance_ids (FWUPD_DEVICE (self)); + + /* OEM specific hardware */ + if (fu_device_has_flag (self, FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS)) + return; + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index (instance_ids, i); + g_autofree gchar *guid = fwupd_guid_hash_string (instance_id); + fwupd_device_add_guid (FWUPD_DEVICE (self), guid); + } + + /* convert all children too */ + for (guint i = 0; i < priv->children->len; i++) { + FuDevice *devtmp = g_ptr_array_index (priv->children, i); + fu_device_convert_instance_ids (devtmp); + } +} + +/** + * fu_device_setup: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Sets up a device, setting parameters on the object that requires + * the device to be open and have the interface claimed. + * If the device is not compatible then an error should be returned. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_setup (FuDevice *self, GError **error) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* already done */ + if (priv->done_setup) + return TRUE; + + /* subclassed */ + if (klass->setup != NULL) { + if (!klass->setup (self, error)) + return FALSE; + } + + /* convert the instance IDs to GUIDs */ + fu_device_convert_instance_ids (self); + + priv->done_setup = TRUE; + return TRUE; +} + +/** + * fu_device_activate: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Activates up a device, which normally means the device switches to a new + * firmware version. This should only be called when data loss cannot occur. + * + * Returns: %TRUE for success + * + * Since: 1.2.6 + **/ +gboolean +fu_device_activate (FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* subclassed */ + if (klass->activate != NULL) { + if (!klass->activate (self, error)) + return FALSE; + } + + return TRUE; +} + +/** + * fu_device_probe_invalidate: + * @self: A #FuDevice + * + * Normally when calling fu_device_probe() multiple times it is only done once. + * Calling this method causes the next requests to fu_device_probe() and + * fu_device_setup() actually probe the hardware. + * + * This should be done in case the backing device has changed, for instance if + * a USB device has been replugged. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +void +fu_device_probe_invalidate (FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_DEVICE (self)); + priv->done_probe = FALSE; + priv->done_setup = FALSE; +} + +/** + * fu_device_incorporate: + * @self: A #FuDevice + * @donor: Another #FuDevice + * + * Copy all properties from the donor object if they have not already been set. + * + * Since: 1.1.0 + **/ +void +fu_device_incorporate (FuDevice *self, FuDevice *donor) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + FuDevicePrivate *priv = GET_PRIVATE (self); + FuDevicePrivate *priv_donor = GET_PRIVATE (donor); + GPtrArray *parent_guids = fu_device_get_parent_guids (donor); + g_autoptr(GList) metadata_keys = NULL; + + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (FU_IS_DEVICE (donor)); + + /* copy from donor FuDevice if has not already been set */ + if (priv->alternate_id == NULL) + fu_device_set_alternate_id (self, fu_device_get_alternate_id (donor)); + if (priv->equivalent_id == NULL) + fu_device_set_equivalent_id (self, fu_device_get_equivalent_id (donor)); + if (priv->quirks == NULL) + fu_device_set_quirks (self, fu_device_get_quirks (donor)); + g_rw_lock_reader_lock (&priv_donor->parent_guids_mutex); + for (guint i = 0; i < parent_guids->len; i++) + fu_device_add_parent_guid (self, g_ptr_array_index (parent_guids, i)); + g_rw_lock_reader_unlock (&priv_donor->parent_guids_mutex); + g_rw_lock_reader_lock (&priv_donor->metadata_mutex); + metadata_keys = g_hash_table_get_keys (priv_donor->metadata); + for (GList *l = metadata_keys; l != NULL; l = l->next) { + const gchar *key = l->data; + if (g_hash_table_lookup (priv->metadata, key) == NULL) { + const gchar *value = g_hash_table_lookup (priv_donor->metadata, key); + fu_device_set_metadata (self, key, value); + } + } + g_rw_lock_reader_unlock (&priv_donor->metadata_mutex); + + /* now the base class, where all the interesting bits are */ + fwupd_device_incorporate (FWUPD_DEVICE (self), FWUPD_DEVICE (donor)); + + /* optional subclass */ + if (klass->incorporate != NULL) + klass->incorporate (self, donor); +} + +/** + * fu_device_incorporate_from_component: + * @device: A #FuDevice + * @component: A #XbNode + * + * Copy all properties from the donor AppStream component. + * + * Since: 1.2.4 + **/ +void +fu_device_incorporate_from_component (FuDevice *self, XbNode *component) +{ + const gchar *tmp; + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (XB_IS_NODE (component)); + tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateMessage']", NULL); + if (tmp != NULL) + fwupd_device_set_update_message (FWUPD_DEVICE (self), tmp); } static void @@ -1114,17 +2297,22 @@ G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_STATUS, pspec); + pspec = g_param_spec_string ("physical-id", NULL, NULL, NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_PHYSICAL_ID, pspec); + + pspec = g_param_spec_string ("logical-id", NULL, NULL, NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_LOGICAL_ID, pspec); + pspec = g_param_spec_uint ("progress", NULL, NULL, 0, 100, 0, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_PROGRESS, pspec); - pspec = g_param_spec_string ("platform-id", NULL, NULL, NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME); - g_object_class_install_property (object_class, PROP_PLATFORM_ID, pspec); - pspec = g_param_spec_object ("quirks", NULL, NULL, FU_TYPE_QUIRKS, G_PARAM_READWRITE | @@ -1133,21 +2321,23 @@ } static void -fu_device_init (FuDevice *device) +fu_device_init (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (device); + FuDevicePrivate *priv = GET_PRIVATE (self); priv->status = FWUPD_STATUS_IDLE; priv->children = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); priv->parent_guids = g_ptr_array_new_with_free_func (g_free); + g_rw_lock_init (&priv->parent_guids_mutex); priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + g_rw_lock_init (&priv->metadata_mutex); } static void fu_device_finalize (GObject *object) { - FuDevice *device = FU_DEVICE (object); - FuDevicePrivate *priv = GET_PRIVATE (device); + FuDevice *self = FU_DEVICE (object); + FuDevicePrivate *priv = GET_PRIVATE (self); if (priv->alternate != NULL) g_object_unref (priv->alternate); @@ -1155,9 +2345,14 @@ g_object_remove_weak_pointer (G_OBJECT (priv->parent), (gpointer *) &priv->parent); if (priv->quirks != NULL) g_object_unref (priv->quirks); + if (priv->poll_id != 0) + g_source_remove (priv->poll_id); + g_rw_lock_clear (&priv->metadata_mutex); + g_rw_lock_clear (&priv->parent_guids_mutex); g_hash_table_unref (priv->metadata); g_ptr_array_unref (priv->children); g_ptr_array_unref (priv->parent_guids); + g_free (priv->alternate_id); g_free (priv->equivalent_id); G_OBJECT_CLASS (fu_device_parent_class)->finalize (object); @@ -1166,7 +2361,6 @@ FuDevice * fu_device_new (void) { - FuDevice *device; - device = g_object_new (FU_TYPE_DEVICE, NULL); - return FU_DEVICE (device); + FuDevice *self = g_object_new (FU_TYPE_DEVICE, NULL); + return FU_DEVICE (self); } diff -Nru fwupd-1.0.9/src/fu-device.h fwupd-1.2.10/src/fu-device.h --- fwupd-1.0.9/src/fu-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,17 +1,16 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_DEVICE_H -#define __FU_DEVICE_H +#pragma once #include #include #include "fu-quirks.h" +#include "fu-common-version.h" G_BEGIN_DECLS @@ -21,19 +20,42 @@ struct _FuDeviceClass { FwupdDeviceClass parent_class; - void (*to_string) (FuDevice *device, + void (*to_string) (FuDevice *self, GString *str); - gboolean (*write_firmware) (FuDevice *device, + gboolean (*write_firmware) (FuDevice *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); + GBytes *(*read_firmware) (FuDevice *self, + GError **error); + gboolean (*detach) (FuDevice *self, + GError **error); + gboolean (*attach) (FuDevice *self, + GError **error); + gboolean (*open) (FuDevice *self, + GError **error); + gboolean (*close) (FuDevice *self, + GError **error); + gboolean (*probe) (FuDevice *self, + GError **error); + GBytes *(*prepare_firmware) (FuDevice *self, GBytes *fw, + FwupdInstallFlags flags, GError **error); - GBytes *(*read_firmware) (FuDevice *device, + gboolean (*set_quirk_kv) (FuDevice *self, + const gchar *key, + const gchar *value, + GError **error); + gboolean (*setup) (FuDevice *self, GError **error); - gboolean (*detach) (FuDevice *device, + void (*incorporate) (FuDevice *self, + FuDevice *donor); + gboolean (*poll) (FuDevice *self, GError **error); - gboolean (*attach) (FuDevice *device, + gboolean (*activate) (FuDevice *self, GError **error); /*< private >*/ - gpointer padding[28]; + gpointer padding[19]; }; /** @@ -52,7 +74,7 @@ * being slow and clumsy. This should be used when the user has to do something, * e.g. unplug, press a magic button and then replug. */ -#define FU_DEVICE_REMOVE_DELAY_USER_REPLUG 20000 +#define FU_DEVICE_REMOVE_DELAY_USER_REPLUG 40000 FuDevice *fu_device_new (void); @@ -60,23 +82,26 @@ #define fu_device_add_flag(d,v) fwupd_device_add_flag(FWUPD_DEVICE(d),v) #define fu_device_remove_flag(d,v) fwupd_device_remove_flag(FWUPD_DEVICE(d),v) #define fu_device_has_flag(d,v) fwupd_device_has_flag(FWUPD_DEVICE(d),v) +#define fu_device_has_instance_id(d,v) fwupd_device_has_instance_id(FWUPD_DEVICE(d),v) #define fu_device_add_checksum(d,v) fwupd_device_add_checksum(FWUPD_DEVICE(d),v) +#define fu_device_add_release(d,v) fwupd_device_add_release(FWUPD_DEVICE(d),v) #define fu_device_add_icon(d,v) fwupd_device_add_icon(FWUPD_DEVICE(d),v) #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) #define fu_device_set_summary(d,v) fwupd_device_set_summary(FWUPD_DEVICE(d),v) #define fu_device_set_update_error(d,v) fwupd_device_set_update_error(FWUPD_DEVICE(d),v) #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_version_format(d,v) fwupd_device_set_version_format(FWUPD_DEVICE(d),v) #define fu_device_set_flashes_left(d,v) fwupd_device_set_flashes_left(FWUPD_DEVICE(d),v) +#define fu_device_set_install_duration(d,v) fwupd_device_set_install_duration(FWUPD_DEVICE(d),v) #define fu_device_get_checksums(d) fwupd_device_get_checksums(FWUPD_DEVICE(d)) #define fu_device_get_flags(d) fwupd_device_get_flags(FWUPD_DEVICE(d)) #define fu_device_get_created(d) fwupd_device_get_created(FWUPD_DEVICE(d)) @@ -85,6 +110,8 @@ #define fu_device_get_guid_default(d) fwupd_device_get_guid_default(FWUPD_DEVICE(d)) #define fu_device_get_icons(d) fwupd_device_get_icons(FWUPD_DEVICE(d)) #define fu_device_get_name(d) fwupd_device_get_name(FWUPD_DEVICE(d)) +#define fu_device_get_serial(d) fwupd_device_get_serial(FWUPD_DEVICE(d)) +#define fu_device_get_summary(d) fwupd_device_get_summary(FWUPD_DEVICE(d)) #define fu_device_get_id(d) fwupd_device_get_id(FWUPD_DEVICE(d)) #define fu_device_get_plugin(d) fwupd_device_get_plugin(FWUPD_DEVICE(d)) #define fu_device_get_update_error(d) fwupd_device_get_update_error(FWUPD_DEVICE(d)) @@ -93,81 +120,122 @@ #define fu_device_get_version(d) fwupd_device_get_version(FWUPD_DEVICE(d)) #define fu_device_get_version_lowest(d) fwupd_device_get_version_lowest(FWUPD_DEVICE(d)) #define fu_device_get_version_bootloader(d) fwupd_device_get_version_bootloader(FWUPD_DEVICE(d)) +#define fu_device_get_version_format(d) fwupd_device_get_version_format(FWUPD_DEVICE(d)) #define fu_device_get_vendor_id(d) fwupd_device_get_vendor_id(FWUPD_DEVICE(d)) #define fu_device_get_flashes_left(d) fwupd_device_get_flashes_left(FWUPD_DEVICE(d)) +#define fu_device_get_install_duration(d) fwupd_device_get_install_duration(FWUPD_DEVICE(d)) /* accessors */ -gchar *fu_device_to_string (FuDevice *device); -const gchar *fu_device_get_equivalent_id (FuDevice *device); -void fu_device_set_equivalent_id (FuDevice *device, +gchar *fu_device_to_string (FuDevice *self); +const gchar *fu_device_get_alternate_id (FuDevice *self); +void fu_device_set_alternate_id (FuDevice *self, + const gchar *alternate_id); +const gchar *fu_device_get_equivalent_id (FuDevice *self); +void fu_device_set_equivalent_id (FuDevice *self, const gchar *equivalent_id); -void fu_device_add_guid (FuDevice *device, +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 *device); -FuDevice *fu_device_get_alternate (FuDevice *device); -void fu_device_set_alternate (FuDevice *device, - FuDevice *alternate); -FuDevice *fu_device_get_parent (FuDevice *device); -GPtrArray *fu_device_get_children (FuDevice *device); -void fu_device_add_child (FuDevice *device, +void fu_device_add_instance_id (FuDevice *self, + const gchar *instance_id); +gchar *fu_device_get_guids_as_str (FuDevice *self); +FuDevice *fu_device_get_alternate (FuDevice *self); +FuDevice *fu_device_get_parent (FuDevice *self); +GPtrArray *fu_device_get_children (FuDevice *self); +void fu_device_add_child (FuDevice *self, FuDevice *child); -void fu_device_add_parent_guid (FuDevice *device, +void fu_device_add_parent_guid (FuDevice *self, const gchar *guid); -const gchar *fu_device_get_metadata (FuDevice *device, +void fu_device_add_counterpart_guid (FuDevice *self, + const gchar *guid); +const gchar *fu_device_get_metadata (FuDevice *self, const gchar *key); -gboolean fu_device_get_metadata_boolean (FuDevice *device, +gboolean fu_device_get_metadata_boolean (FuDevice *self, const gchar *key); -guint fu_device_get_metadata_integer (FuDevice *device, +guint fu_device_get_metadata_integer (FuDevice *self, const gchar *key); -void fu_device_set_metadata (FuDevice *device, +void fu_device_set_metadata (FuDevice *self, const gchar *key, const gchar *value); -void fu_device_set_metadata_boolean (FuDevice *device, +void fu_device_set_metadata_boolean (FuDevice *self, const gchar *key, gboolean value); -void fu_device_set_metadata_integer (FuDevice *device, +void fu_device_set_metadata_integer (FuDevice *self, const gchar *key, guint value); -void fu_device_set_id (FuDevice *device, +void fu_device_set_id (FuDevice *self, const gchar *id); -const gchar *fu_device_get_platform_id (FuDevice *device); -void fu_device_set_platform_id (FuDevice *device, - const gchar *platform_id); -const gchar *fu_device_get_serial (FuDevice *device); -void fu_device_set_serial (FuDevice *device, - const gchar *serial); -const gchar *fu_device_get_plugin_hints (FuDevice *device); -void fu_device_set_plugin_hints (FuDevice *device, - const gchar *plugin_hints); -void fu_device_set_name (FuDevice *device, +void fu_device_set_version (FuDevice *self, + const gchar *version, + FwupdVersionFormat fmt); +const gchar *fu_device_get_physical_id (FuDevice *self); +void fu_device_set_physical_id (FuDevice *self, + const gchar *physical_id); +const gchar *fu_device_get_logical_id (FuDevice *self); +void fu_device_set_logical_id (FuDevice *self, + const gchar *logical_id); +const gchar *fu_device_get_custom_flags (FuDevice *self); +gboolean fu_device_has_custom_flag (FuDevice *self, + const gchar *hint); +void fu_device_set_custom_flags (FuDevice *self, + const gchar *custom_flags); +void fu_device_set_name (FuDevice *self, const gchar *value); -guint fu_device_get_remove_delay (FuDevice *device); -void fu_device_set_remove_delay (FuDevice *device, +guint fu_device_get_remove_delay (FuDevice *self); +void fu_device_set_remove_delay (FuDevice *self, guint remove_delay); -FwupdStatus fu_device_get_status (FuDevice *device); -void fu_device_set_status (FuDevice *device, +FwupdStatus fu_device_get_status (FuDevice *self); +void fu_device_set_status (FuDevice *self, FwupdStatus status); -guint fu_device_get_progress (FuDevice *device); -void fu_device_set_progress (FuDevice *device, +void fu_device_set_firmware_size (FuDevice *self, + guint64 size); +void fu_device_set_firmware_size_min (FuDevice *self, + guint64 size_min); +void fu_device_set_firmware_size_max (FuDevice *self, + guint64 size_max); +guint64 fu_device_get_firmware_size_min (FuDevice *self); +guint64 fu_device_get_firmware_size_max (FuDevice *self); +guint fu_device_get_progress (FuDevice *self); +void fu_device_set_progress (FuDevice *self, guint progress); -void fu_device_set_progress_full (FuDevice *device, +void fu_device_set_progress_full (FuDevice *self, gsize progress_done, gsize progress_total); -void fu_device_set_quirks (FuDevice *device, +void fu_device_set_quirks (FuDevice *self, FuQuirks *quirks); -FuQuirks *fu_device_get_quirks (FuDevice *device); -FwupdRelease *fu_device_get_release_default (FuDevice *device); -gboolean fu_device_write_firmware (FuDevice *device, +FuQuirks *fu_device_get_quirks (FuDevice *self); +FwupdRelease *fu_device_get_release_default (FuDevice *self); +gboolean fu_device_write_firmware (FuDevice *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); +GBytes *fu_device_prepare_firmware (FuDevice *self, GBytes *fw, + FwupdInstallFlags flags, + GError **error); +GBytes *fu_device_read_firmware (FuDevice *self, + GError **error); +gboolean fu_device_attach (FuDevice *self, + GError **error); +gboolean fu_device_detach (FuDevice *self, GError **error); -GBytes *fu_device_read_firmware (FuDevice *device, +void fu_device_incorporate (FuDevice *self, + FuDevice *donor); +gboolean fu_device_open (FuDevice *self, GError **error); -gboolean fu_device_attach (FuDevice *device, +gboolean fu_device_close (FuDevice *self, GError **error); -gboolean fu_device_detach (FuDevice *device, +gboolean fu_device_probe (FuDevice *self, GError **error); +gboolean fu_device_setup (FuDevice *self, + GError **error); +gboolean fu_device_activate (FuDevice *self, + GError **error); +void fu_device_probe_invalidate (FuDevice *self); +gboolean fu_device_poll (FuDevice *self, + GError **error); +void fu_device_set_poll_interval (FuDevice *self, + guint interval); G_END_DECLS - -#endif /* __FU_DEVICE_H */ - diff -Nru fwupd-1.0.9/src/fu-device-list.c fwupd-1.2.10/src/fu-device-list.c --- fwupd-1.0.9/src/fu-device-list.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device-list.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuDeviceList" + #include "config.h" #include @@ -12,6 +13,7 @@ #include "fu-device-list.h" #include "fu-device-private.h" +#include "fu-mutex.h" #include "fwupd-error.h" @@ -35,6 +37,7 @@ { GObject parent_instance; GPtrArray *devices; /* of FuDeviceItem */ + GRWLock devices_mutex; }; enum { @@ -50,6 +53,8 @@ FuDevice *device; FuDevice *device_old; FuDeviceList *self; /* no ref */ + GMainLoop *replug_loop; /* block waiting for replug */ + guint replug_id; /* timeout the loop */ guint remove_id; } FuDeviceItem; @@ -94,6 +99,8 @@ GPtrArray *devices; g_return_val_if_fail (FU_IS_DEVICE_LIST (self), NULL); devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + + g_rw_lock_reader_lock (&self->devices_mutex); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item = g_ptr_array_index (self->devices, i); g_ptr_array_add (devices, g_object_ref (item->device)); @@ -104,6 +111,7 @@ continue; g_ptr_array_add (devices, g_object_ref (item->device_old)); } + g_rw_lock_reader_unlock (&self->devices_mutex); return devices; } @@ -112,7 +120,7 @@ * @self: A #FuDeviceList * * Returns all the active devices that have been added to the device list. - * An active device is defined as a device that is currently conected and has + * An active device is defined as a device that is currently connected and has * is owned by a plugin. * * Returns: (transfer container) (element-type FuDevice): the devices @@ -125,16 +133,20 @@ GPtrArray *devices; g_return_val_if_fail (FU_IS_DEVICE_LIST (self), NULL); devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_rw_lock_reader_lock (&self->devices_mutex); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item = g_ptr_array_index (self->devices, i); g_ptr_array_add (devices, g_object_ref (item->device)); } + g_rw_lock_reader_unlock (&self->devices_mutex); return devices; } static FuDeviceItem * fu_device_list_find_by_device (FuDeviceList *self, FuDevice *device) { + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->devices_mutex); + g_return_val_if_fail (locker != NULL, NULL); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item = g_ptr_array_index (self->devices, i); if (item->device == device) @@ -151,6 +163,8 @@ static FuDeviceItem * fu_device_list_find_by_guid (FuDeviceList *self, const gchar *guid) { + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->devices_mutex); + g_return_val_if_fail (locker != NULL, NULL); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item = g_ptr_array_index (self->devices, i); if (fu_device_has_guid (item->device, guid)) @@ -167,6 +181,35 @@ } static FuDeviceItem * +fu_device_list_find_by_connection (FuDeviceList *self, + const gchar *physical_id, + const gchar *logical_id) +{ + g_autoptr(GRWLockReaderLocker) locker = NULL; + if (physical_id == NULL) + return NULL; + locker = g_rw_lock_reader_locker_new (&self->devices_mutex); + g_return_val_if_fail (locker != NULL, NULL); + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item_tmp = g_ptr_array_index (self->devices, i); + FuDevice *device = item_tmp->device; + if (device != NULL && + g_strcmp0 (fu_device_get_physical_id (device), physical_id) == 0 && + g_strcmp0 (fu_device_get_logical_id (device), logical_id) == 0) + return item_tmp; + } + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item_tmp = g_ptr_array_index (self->devices, i); + FuDevice *device = item_tmp->device_old; + if (device != NULL && + g_strcmp0 (fu_device_get_physical_id (device), physical_id) == 0 && + g_strcmp0 (fu_device_get_logical_id (device), logical_id) == 0) + return item_tmp; + } + return NULL; +} + +static FuDeviceItem * fu_device_list_find_by_id (FuDeviceList *self, const gchar *device_id, gboolean *multiple_matches) @@ -174,8 +217,15 @@ FuDeviceItem *item = NULL; gsize device_id_len; + /* sanity check */ + if (device_id == NULL) { + g_critical ("device ID was NULL"); + return NULL; + } + /* support abbreviated hashes */ device_id_len = strlen (device_id); + g_rw_lock_reader_lock (&self->devices_mutex); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item_tmp = g_ptr_array_index (self->devices, i); const gchar *ids[] = { @@ -190,6 +240,12 @@ } } } + g_rw_lock_reader_unlock (&self->devices_mutex); + if (item != NULL) + return item; + + /* only search old devices if we didn't find the active device */ + g_rw_lock_reader_lock (&self->devices_mutex); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item_tmp = g_ptr_array_index (self->devices, i); const gchar *ids[3] = { NULL }; @@ -205,6 +261,7 @@ } } } + g_rw_lock_reader_unlock (&self->devices_mutex); return item; } @@ -215,7 +272,7 @@ * * Returns the old device associated with the currently active device. * - * Returns: (transfer none): the device, or %NULL if not found + * Returns: (transfer full): the device, or %NULL if not found * * Since: 1.0.3 **/ @@ -225,12 +282,16 @@ FuDeviceItem *item = fu_device_list_find_by_device (self, device); if (item == NULL) return NULL; - return item->device_old; + if (item->device_old == NULL) + return NULL; + return g_object_ref (item->device_old); } static FuDeviceItem * fu_device_list_get_by_guids (FuDeviceList *self, GPtrArray *guids) { + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->devices_mutex); + g_return_val_if_fail (locker != NULL, NULL); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item = g_ptr_array_index (self->devices, i); for (guint j = 0; j < guids->len; j++) { @@ -264,10 +325,28 @@ /* just remove now */ g_debug ("doing delayed removal"); fu_device_list_emit_device_removed (self, item->device); + g_rw_lock_writer_lock (&self->devices_mutex); g_ptr_array_remove (self->devices, item); + g_rw_lock_writer_unlock (&self->devices_mutex); return G_SOURCE_REMOVE; } +static void +fu_device_list_remove_with_delay (FuDeviceItem *item) +{ + /* we can't do anything with an unconnected device */ + fu_device_remove_flag (item->device, FWUPD_DEVICE_FLAG_UPDATABLE); + + /* give the hardware time to re-enumerate or the user time to + * re-insert the device with a magic button pressed */ + g_debug ("waiting %ums for %s device removal", + fu_device_get_remove_delay (item->device), + fu_device_get_name (item->device)); + item->remove_id = g_timeout_add (fu_device_get_remove_delay (item->device), + fu_device_list_device_delayed_remove_cb, + item); +} + /** * fu_device_list_remove: * @self: A #FuDeviceList @@ -288,6 +367,7 @@ fu_device_list_remove (FuDeviceList *self, FuDevice *device) { FuDeviceItem *item; + GPtrArray *children; g_return_if_fail (FU_IS_DEVICE_LIST (self)); g_return_if_fail (FU_IS_DEVICE (device)); @@ -305,25 +385,38 @@ item->remove_id = 0; } + /* remove any children associated with device */ + children = fu_device_get_children (device); + for (guint j = 0; j < children->len; j++) { + FuDevice *child = g_ptr_array_index (children, j); + FuDeviceItem *child_item = fu_device_list_find_by_id (self, + fu_device_get_id (child), + NULL); + if (child_item == NULL) { + g_debug ("device %s not found", fu_device_get_id (child)); + continue; + } + if (fu_device_get_remove_delay (child_item->device) > 0) { + fu_device_list_remove_with_delay (child_item); + continue; + } + fu_device_list_emit_device_removed (self, child); + g_rw_lock_writer_lock (&self->devices_mutex); + g_ptr_array_remove (self->devices, child_item); + g_rw_lock_writer_unlock (&self->devices_mutex); + } + /* delay the removal and check for replug */ if (fu_device_get_remove_delay (item->device) > 0) { - - /* we can't do anything with an unconnected device */ - fu_device_remove_flag (item->device, FWUPD_DEVICE_FLAG_UPDATABLE); - - /* give the hardware time to re-enumerate or the user time to - * re-insert the device with a magic button pressed */ - g_debug ("waiting %ums for device removal", - fu_device_get_remove_delay (item->device)); - item->remove_id = g_timeout_add (fu_device_get_remove_delay (item->device), - fu_device_list_device_delayed_remove_cb, - item); + fu_device_list_remove_with_delay (item); return; } /* remove right now */ fu_device_list_emit_device_removed (self, item->device); + g_rw_lock_writer_lock (&self->devices_mutex); g_ptr_array_remove (self->devices, item); + g_rw_lock_writer_unlock (&self->devices_mutex); } static void @@ -334,11 +427,75 @@ const gchar *guid_tmp = g_ptr_array_index (guids_old, i); if (!fu_device_has_guid (device_new, guid_tmp)) { g_debug ("adding GUID %s to device", guid_tmp); - fu_device_add_guid (device_new, guid_tmp); + fu_device_add_counterpart_guid (device_new, guid_tmp); } } } +static void +fu_device_list_replace (FuDeviceList *self, FuDeviceItem *item, FuDevice *device) +{ + /* clear timeout if scheduled */ + if (item->remove_id != 0) { + g_source_remove (item->remove_id); + item->remove_id = 0; + } + + /* copy over any GUIDs that used to exist */ + fu_device_list_add_missing_guids (device, item->device); + + /* enforce the vendor ID if specified */ + if (fu_device_get_vendor_id (item->device) != NULL && + fu_device_get_vendor_id (device) == NULL) { + const gchar *vendor_id = fu_device_get_vendor_id (item->device); + g_debug ("copying old vendor ID %s to new device", vendor_id); + fu_device_set_vendor_id (device, vendor_id); + } + + /* copy over the version strings if not set */ + if (fu_device_get_version (item->device) != NULL && + fu_device_get_version (device) == NULL) { + const gchar *version = fu_device_get_version (item->device); + g_debug ("copying old version %s to new device", version); + fu_device_set_version (device, version, + fu_device_get_version_format (item->device)); + } + + /* always use the runtime version */ + if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) && + fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { + const gchar *version = fu_device_get_version (item->device); + g_debug ("forcing runtime version %s to new device", version); + fu_device_set_version (device, version, + fu_device_get_version_format (item->device)); + } + + /* allow another plugin to handle the write too */ + if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED)) { + g_debug ("copying another-write-required to new device"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + } + + /* copy the parent if not already set */ + if (fu_device_get_parent (item->device) != NULL && + fu_device_get_parent (device) == NULL) { + FuDevice *parent = fu_device_get_parent (item->device); + g_debug ("copying parent %s to new device", fu_device_get_id (parent)); + fu_device_set_parent (device, parent); + } + + /* assign the new device */ + g_set_object (&item->device_old, item->device); + g_set_object (&item->device, device); + fu_device_list_emit_device_changed (self, device); + + /* we were waiting for this... */ + if (g_main_loop_is_running (item->replug_loop)) { + g_debug ("quitting replug loop"); + g_main_loop_quit (item->replug_loop); + } +} + /** * fu_device_list_add: * @self: A #FuDeviceList @@ -378,7 +535,7 @@ g_source_remove (item->remove_id); item->remove_id = 0; } - fu_device_list_emit_device_changed (self, device); + fu_device_list_replace (self, item, device); return; } @@ -391,56 +548,56 @@ /* verify a compatible device does not already exist */ item = fu_device_list_get_by_guids (self, fu_device_get_guids (device)); + if (item == NULL) { + item = fu_device_list_find_by_connection (self, + fu_device_get_physical_id (device), + fu_device_get_logical_id (device)); + } if (item != NULL && item->remove_id != 0) { g_debug ("found compatible device %s recently removed, reusing " "item from plugin %s for plugin %s", fu_device_get_id (item->device), fu_device_get_plugin (item->device), fu_device_get_plugin (device)); + fu_device_list_replace (self, item, device); + return; + } - /* do not remove this device */ - g_source_remove (item->remove_id); - item->remove_id = 0; - - /* copy over any GUIDs that used to exist */ - fu_device_list_add_missing_guids (device, item->device); - - /* enforce the vendor ID if specified */ - if (fu_device_get_vendor_id (item->device) != NULL && - fu_device_get_vendor_id (device) == NULL) { - const gchar *vendor_id = fu_device_get_vendor_id (item->device); - g_debug ("copying old vendor ID %s to new device", vendor_id); - fu_device_set_vendor_id (device, vendor_id); - } - - /* copy over the version strings if not set */ - if (fu_device_get_version (item->device) != NULL && - fu_device_get_version (device) == NULL) { - const gchar *version = fu_device_get_version (item->device); - g_debug ("copying old version %s to new device", version); - fu_device_set_version (device, version); + /* added the same device from a different plugin */ + if (item != NULL && g_strcmp0 (fu_device_get_plugin (item->device), + fu_device_get_plugin (device)) != 0) { + if (fu_device_get_priority (device) < fu_device_get_priority (item->device)) { + g_debug ("ignoring device %s [%s] as better device %s [%s] already exists", + fu_device_get_id (device), + fu_device_get_plugin (device), + fu_device_get_id (item->device), + fu_device_get_plugin (item->device)); + return; } - - /* always use the runtime version */ - if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) && - fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { - const gchar *version = fu_device_get_version (item->device); - g_debug ("forcing runtime version %s to new device", version); - fu_device_set_version (device, version); + if (fu_device_get_priority (device) == fu_device_get_priority (item->device)) { + g_warning ("ignoring device %s [%s] existing device %s [%s] already exists", + fu_device_get_id (device), + fu_device_get_plugin (device), + fu_device_get_id (item->device), + fu_device_get_plugin (item->device)); + return; } - - /* assign the new device */ - g_set_object (&item->device_old, item->device); - g_set_object (&item->device, device); - fu_device_list_emit_device_changed (self, device); - return; + g_debug ("removing device %s [%s] as better device %s [%s] added", + fu_device_get_id (item->device), + fu_device_get_plugin (item->device), + fu_device_get_id (device), + fu_device_get_plugin (device)); + fu_device_list_remove (self, item->device); } /* add helper */ item = g_new0 (FuDeviceItem, 1); item->self = self; /* no ref */ item->device = g_object_ref (device); + item->replug_loop = g_main_loop_new (NULL, FALSE); + g_rw_lock_writer_lock (&self->devices_mutex); g_ptr_array_add (self->devices, item); + g_rw_lock_writer_unlock (&self->devices_mutex); fu_device_list_emit_device_added (self, device); } @@ -452,7 +609,7 @@ * * Finds a specific device that has the matching GUID. * - * Returns: (transfer none): a device, or %NULL if not found + * Returns: (transfer full): a device, or %NULL if not found * * Since: 1.0.2 **/ @@ -465,7 +622,7 @@ g_return_val_if_fail (error == NULL || *error == NULL, NULL); item = fu_device_list_find_by_guid (self, guid); if (item != NULL) - return item->device; + return g_object_ref (item->device); g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, @@ -474,6 +631,90 @@ return NULL; } +static gboolean +fu_device_list_replug_cb (gpointer user_data) +{ + FuDeviceItem *item = (FuDeviceItem *) user_data; + + /* no longer valid */ + item->replug_id = 0; + + /* quit loop */ + g_debug ("device did not replug"); + g_main_loop_quit (item->replug_loop); + return FALSE; +} + +/** + * fu_device_list_wait_for_replug: + * @self: A #FuDeviceList + * @device: A #FuDevice + * @error: A #GError, or %NULL + * + * Waits for a specific device to replug if %FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG + * is set. + * + * If the device does not exist this function returns without an error. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_list_wait_for_replug (FuDeviceList *self, FuDevice *device, GError **error) +{ + FuDeviceItem *item; + guint remove_delay; + + g_return_val_if_fail (FU_IS_DEVICE_LIST (self), FALSE); + g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* not found */ + item = fu_device_list_find_by_device (self, device); + if (item == NULL) + return TRUE; + + /* not required, or possibly literally just happened */ + if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) { + g_debug ("no replug or re-enumerate required"); + return TRUE; + } + + /* plugin did not specify */ + remove_delay = fu_device_get_remove_delay (device); + if (remove_delay == 0) { + remove_delay = FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE; + g_warning ("plugin %s did not specify a remove delay for %s, " + "so guessing we should wait %ums for replug", + fu_device_get_plugin (device), + fu_device_get_id (device), + remove_delay); + } else { + g_debug ("waiting %ums for replug", remove_delay); + } + + /* time to unplug and then re-plug */ + item->replug_id = g_timeout_add (remove_delay, fu_device_list_replug_cb, item); + g_main_loop_run (item->replug_loop); + + /* the loop was quit without the timer */ + if (item->replug_id != 0) { + g_debug ("waited for replug"); + g_source_remove (item->replug_id); + item->replug_id = 0; + return TRUE; + } + + /* device was not added back to the device list */ + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "device %s did not come back", + fu_device_get_id (device)); + return FALSE; +} + /** * fu_device_list_get_by_id: * @self: A #FuDeviceList @@ -483,7 +724,7 @@ * Finds a specific device using the ID string. This function also supports * using abbreviated hashes. * - * Returns: (transfer none): a device, or %NULL if not found + * Returns: (transfer full): a device, or %NULL if not found * * Since: 1.0.2 **/ @@ -519,7 +760,7 @@ } /* something found */ - return item->device; + return g_object_ref (item->device); } static void @@ -527,8 +768,11 @@ { if (item->remove_id != 0) g_source_remove (item->remove_id); + if (item->replug_id != 0) + g_source_remove (item->replug_id); if (item->device_old != NULL) g_object_unref (item->device_old); + g_main_loop_unref (item->replug_loop); g_object_unref (item->device); g_free (item); } @@ -560,6 +804,7 @@ fu_device_list_init (FuDeviceList *self) { self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_device_list_item_free); + g_rw_lock_init (&self->devices_mutex); } static void @@ -568,6 +813,7 @@ FuDeviceList *self = FU_DEVICE_LIST (obj); g_ptr_array_unref (self->devices); + g_rw_lock_clear (&self->devices_mutex); G_OBJECT_CLASS (fu_device_list_parent_class)->finalize (obj); } diff -Nru fwupd-1.0.9/src/fu-device-list.h fwupd-1.2.10/src/fu-device-list.h --- fwupd-1.0.9/src/fu-device-list.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device-list.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,19 +1,17 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_DEVICE_LIST_H -#define __FU_DEVICE_LIST_H - -G_BEGIN_DECLS +#pragma once #include #include "fu-device.h" +G_BEGIN_DECLS + #define FU_TYPE_DEVICE_LIST (fu_device_list_get_type ()) G_DECLARE_FINAL_TYPE (FuDeviceList, fu_device_list, FU, DEVICE_LIST, GObject) @@ -32,8 +30,8 @@ FuDevice *fu_device_list_get_by_guid (FuDeviceList *self, const gchar *guid, GError **error); +gboolean fu_device_list_wait_for_replug (FuDeviceList *self, + FuDevice *device, + GError **error); G_END_DECLS - -#endif /* __FU_DEVICE_LIST_H */ - diff -Nru fwupd-1.0.9/src/fu-device-locker.c fwupd-1.2.10/src/fu-device-locker.c --- fwupd-1.0.9/src/fu-device-locker.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device-locker.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuDeviceLocker" + #include "config.h" #include @@ -73,8 +74,8 @@ * manually closed using g_clear_object(). * * The functions used for opening and closing the device are set automatically. - * If the @device is not a type or supertype of #GUsbDevice or #FuUsbDevice - * then this function will not work. + * If the @device is not a type or supertype of #GUsbDevice or #FuDevice then + * this function will not work. * * For custom objects please use fu_device_locker_new_full(). * @@ -98,11 +99,11 @@ error); } - /* FuUsbDevice */ - if (FU_IS_USB_DEVICE (device)) { + /* FuDevice */ + if (FU_IS_DEVICE (device)) { return fu_device_locker_new_full (device, - (FuDeviceLockerFunc) fu_usb_device_open, - (FuDeviceLockerFunc) fu_usb_device_close, + (FuDeviceLockerFunc) fu_device_open, + (FuDeviceLockerFunc) fu_device_close, error); } g_set_error_literal (error, diff -Nru fwupd-1.0.9/src/fu-device-locker.h fwupd-1.2.10/src/fu-device-locker.h --- fwupd-1.0.9/src/fu-device-locker.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device-locker.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_DEVICE_LOCKER_H -#define __FU_DEVICE_LOCKER_H +#pragma once #include @@ -27,5 +25,3 @@ GError **error); G_END_DECLS - -#endif /* __FU_DEVICE_LOCKER_H */ diff -Nru fwupd-1.0.9/src/fu-device-metadata.h fwupd-1.2.10/src/fu-device-metadata.h --- fwupd-1.0.9/src/fu-device-metadata.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device-metadata.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,14 @@ -/* -*- mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Mario Limonciello * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_DEVICE_METADATA_H__ -#define __FU_DEVICE_METADATA_H__ +#pragma once + +#include + +G_BEGIN_DECLS /** * SECTION:fu-device-metadata @@ -34,4 +36,32 @@ */ #define FU_DEVICE_METADATA_TBT_IS_SAFE_MODE "Thunderbolt::IsSafeMode" -#endif /* __FU_DEVICE_METADATA_H__ */ +/** + * FU_DEVICE_METADATA_UEFI_DEVICE_KIND: + * + * The type of UEFI device, e.g. "system-firmware" or "device-firmware" + * Consumed by the uefi plugin when other devices register fake devices that + * need to be handled as a capsule update. + */ +#define FU_DEVICE_METADATA_UEFI_DEVICE_KIND "UefiDeviceKind" + +/** + * FU_DEVICE_METADATA_UEFI_FW_VERSION: + * + * The firmware version of the UEFI device specified as a 32 bit unsigned + * integer. + * Consumed by the uefi plugin when other devices register fake devices that + * need to be handled as a capsule update. + */ +#define FU_DEVICE_METADATA_UEFI_FW_VERSION "UefiFwVersion" + +/** + * FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS: + * + * The capsule flags for the UEFI device, e.g. %EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET + * Consumed by the uefi plugin when other devices register fake devices that + * need to be handled as a capsule update. + */ +#define FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS "UefiCapsuleFlags" + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-device-private.h fwupd-1.2.10/src/fu-device-private.h --- fwupd-1.0.9/src/fu-device-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-device-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,25 +1,50 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_DEVICE_PRIVATE_H -#define __FU_DEVICE_PRIVATE_H +#pragma once #include +#include G_BEGIN_DECLS -GPtrArray *fu_device_get_parent_guids (FuDevice *device); -gboolean fu_device_has_parent_guid (FuDevice *device, +/** + * FuDeviceInstanceFlags: + * @FU_DEVICE_INSTANCE_FLAG_NONE: No flags set + * @FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS: Only use instance ID for quirk matching + * + * The flags to use when interacting with a device instance + **/ +typedef enum { + FU_DEVICE_INSTANCE_FLAG_NONE = 0, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS = 1 << 0, + /*< private >*/ + FU_DEVICE_INSTANCE_FLAG_LAST +} FuDeviceInstanceFlags; + +GPtrArray *fu_device_get_parent_guids (FuDevice *self); +gboolean fu_device_has_parent_guid (FuDevice *self, const gchar *guid); -guint fu_device_get_order (FuDevice *device); -void fu_device_set_order (FuDevice *device, +void fu_device_set_parent (FuDevice *self, + FuDevice *parent); +guint fu_device_get_order (FuDevice *self); +void fu_device_set_order (FuDevice *self, guint order); +guint fu_device_get_priority (FuDevice *self); +void fu_device_set_priority (FuDevice *self, + guint priority); +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); +void fu_device_convert_instance_ids (FuDevice *self); +void fu_device_add_instance_id_full (FuDevice *self, + const gchar *instance_id, + FuDeviceInstanceFlags flags); G_END_DECLS - -#endif /* __FU_DEVICE_PRIVATE_H */ - diff -Nru fwupd-1.0.9/src/fu-engine.c fwupd-1.2.10/src/fu-engine.c --- fwupd-1.0.9/src/fu-engine.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-engine.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,16 +1,17 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuEngine" + #include "config.h" -#include #include #include #include +#include #include #include #include @@ -30,13 +31,22 @@ #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" #include "fu-plugin-list.h" #include "fu-plugin-private.h" #include "fu-quirks.h" #include "fu-smbios.h" +#include "fu-udev-device-private.h" +#include "fu-usb-device-private.h" + +#ifdef HAVE_SYSTEMD +#include "fu-systemd.h" +#endif static void fu_engine_finalize (GObject *obj); @@ -45,24 +55,28 @@ GObject parent_instance; FuAppFlags app_flags; GUsbContext *usb_ctx; + GUdevClient *gudev_client; FuConfig *config; FuDeviceList *device_list; FwupdStatus status; + gboolean tainted; guint percentage; FuHistory *history; - AsProfile *profile; - 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; + GHashTable *approved_firmware; + gboolean loaded; }; enum { @@ -83,6 +97,7 @@ fu_engine_emit_changed (FuEngine *self) { g_signal_emit (self, signals[SIGNAL_CHANGED], 0); + fu_engine_idle_reset (self); } static void @@ -106,20 +121,6 @@ return self->status; } -/** - * fu_engine_profile_dump: - * @self: A #FuEngine - * - * Dumps the engine profiling state to the console. - **/ -void -fu_engine_profile_dump (FuEngine *self) -{ - g_return_if_fail (FU_IS_ENGINE (self)); - as_profile_set_duration_min (self->profile, 1); - as_profile_dump (self->profile); -} - static void fu_engine_set_status (FuEngine *self, FwupdStatus status) { @@ -147,19 +148,23 @@ 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); } static void fu_engine_status_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self) { fu_engine_set_status (self, fu_device_get_status (device)); + fu_engine_emit_device_changed (self, device); } static void fu_engine_watch_device (FuEngine *self, FuDevice *device) { - FuDevice *device_old = fu_device_list_get_old (self->device_list, device); + g_autoptr(FuDevice) device_old = fu_device_list_get_old (self->device_list, device); if (device_old != NULL) { g_signal_handlers_disconnect_by_func (device_old, fu_engine_progress_notify_cb, @@ -182,8 +187,19 @@ } static void +fu_engine_device_runner_device_removed (FuEngine *self, FuDevice *device) +{ + GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list); + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); + fu_plugin_runner_device_removed (plugin_tmp, device); + } +} + +static void fu_engine_device_removed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self) { + fu_engine_device_runner_device_removed (self, device); g_signal_handlers_disconnect_by_data (device, self); g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device); } @@ -195,53 +211,165 @@ fu_engine_emit_device_changed (self, device); } -static const gchar * -_as_release_get_metadata_item (AsRelease *release, const gchar *key) +static gboolean +fu_engine_set_device_version_format (FuEngine *self, FuDevice *device, XbNode *component, GError **error) { - GBytes *blob = as_release_get_blob (release, key); - if (blob == NULL) + FwupdVersionFormat fmt; + const gchar *developer_name; + const gchar *version_format; + + /* specified in metadata */ + version_format = xb_node_query_text (component, + "custom/value[@key='LVFS::VersionFormat']", + NULL); + if (version_format != NULL) { + fmt = fwupd_version_format_from_string (version_format); + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "version format from metadata %s unsupported", + version_format); + return FALSE; + } + g_debug ("using VersionFormat %s from metadata", version_format); + fu_device_set_version_format (device, fmt); + return TRUE; + } + + /* fall back to the SmbiosManufacturer quirk */ + developer_name = xb_node_query_text (component, "developer_name", NULL); + if (developer_name != NULL && + fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INTERNAL)) { + g_autofree gchar *group = NULL; + group = g_strdup_printf ("SmbiosManufacturer=%s", developer_name); + version_format = fu_quirks_lookup_by_id (self->quirks, group, + FU_QUIRKS_UEFI_VERSION_FORMAT); + if (version_format != NULL) { + fmt = fwupd_version_format_from_string (version_format); + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "version format %s from quirk %s unsupported", + version_format, developer_name); + return FALSE; + } + g_debug ("using VersionFormat %s from SmbiosManufacturer %s", + version_format, developer_name); + fu_device_set_version_format (device, fmt); + return TRUE; + } + } + + /* nothing found, which is probably fine */ + return TRUE; +} + +/* convert hex and decimal versions to dotted style */ +static gchar * +fu_engine_get_release_version (FuEngine *self, FuDevice *dev, XbNode *rel, GError **error) +{ + FwupdVersionFormat fmt = FWUPD_VERSION_FORMAT_TRIPLET; + const gchar *version; + guint64 ver_uint32; + + /* get version */ + version = xb_node_get_attr (rel, "version"); + if (version == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "version unset"); + return NULL; + } + + /* already dotted notation */ + if (g_strstr_len (version, -1, ".") != NULL) + return g_strdup (version); + + /* specified in metadata or from a quirk */ + fmt = fu_device_get_version_format (dev); + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "version format unset and version %s ambiguous", + version); return NULL; - return (const gchar *) g_bytes_get_data (blob, NULL); + } + + /* don't touch my version! */ + if (fmt == FWUPD_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 +static gboolean fu_engine_set_release_from_appstream (FuEngine *self, + FuDevice *dev, FwupdRelease *rel, - AsApp *app, - AsRelease *release) + XbNode *component, + XbNode *release, + GError **error) { - AsChecksum *csum; FwupdRemote *remote = NULL; const gchar *tmp; const gchar *remote_id; + guint64 tmp64; + g_autofree gchar *version_rel = NULL; + g_autoptr(GPtrArray) cats = NULL; + g_autoptr(XbNode) description = NULL; + + /* 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); - /* 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)); + /* the version is fixed up at runtime */ + version_rel = fu_engine_get_release_version (self, dev, release, error); + if (version_rel == NULL) + return FALSE; + 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) @@ -249,43 +377,70 @@ 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); - } - 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); + 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); + } + } + tmp64 = xb_node_get_attr_as_uint (release, "install_duration"); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_install_duration (rel, tmp64); + cats = xb_node_query (component, "categories/category", 0, NULL); + if (cats != NULL) { + for (guint i = 0; i < cats->len; i++) { + XbNode *n = g_ptr_array_index (cats, i); + fwupd_release_add_category (rel, xb_node_get_text (n)); + } } - fwupd_release_set_size (rel, as_release_get_size (release, AS_SIZE_KIND_INSTALLED)); + 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); + return TRUE; } -/* 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); } /** @@ -301,8 +456,8 @@ gboolean fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error) { - FuDevice *device; FuPlugin *plugin; + g_autoptr(FuDevice) device = NULL; g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); g_return_val_if_fail (device_id != NULL, FALSE); @@ -330,61 +485,33 @@ return TRUE; } -static AsApp * -fu_engine_verify_update_device_to_app (FuDevice *device) +gboolean +fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, GError **error) { - 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; -} + const gchar *keys[] = { + "ArchiveSizeMax", + "BlacklistDevices", + "BlacklistPlugins", + "IdleTimeout", + "VerboseDomains", + NULL }; -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; + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* 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; + /* check keys are valid */ + if (!g_strv_contains (keys, key)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "key %s not supported", key); + return FALSE; } - return g_steal_pointer (&store); + + /* modify, effective next reboot */ + return fu_config_modify_and_save (self->config, key, value, error); } /** @@ -395,7 +522,7 @@ * @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 **/ @@ -502,26 +629,44 @@ 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 **/ gboolean fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error) { - FuDevice *device; 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); @@ -558,38 +703,70 @@ 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); + if (!fu_common_mkdir_parent (fn, 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); + 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; + + /* 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; } @@ -599,22 +776,24 @@ * @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; - FuDevice *device = NULL; 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); @@ -638,32 +817,42 @@ 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, @@ -673,16 +862,6 @@ 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) { @@ -692,29 +871,52 @@ "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; - } - } - if (hash == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No matching hash kind for %s", version); - return FALSE; + 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); } - if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) { + 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); + } 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; } @@ -722,46 +924,105 @@ 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; + } + + /* 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_not_child (FuEngine *self, XbNode *req, + FuDevice *device, GError **error) +{ + GPtrArray *children = fu_device_get_children (device); + + /* only supported */ + if (g_strcmp0 (xb_node_get_element (req), "firmware") != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot handle not-child %s requirement", + xb_node_get_element (req)); + return FALSE; + } + + /* check each child */ + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + const gchar *version = fu_device_get_version (child); + if (fu_engine_require_vercmp (req, version, NULL)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not compatible with child device version %s", + version); + return FALSE; } } - return apps; -#endif + return TRUE; } 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, @@ -775,15 +1036,15 @@ } /* 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, @@ -797,15 +1058,15 @@ } /* 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, @@ -818,61 +1079,101 @@ return TRUE; } + /* child version */ + if (g_strcmp0 (xb_node_get_text (req), "not-child") == 0) + return fu_engine_check_requirement_not_child (self, req, device, error); + + /* another device */ + if (fwupd_guid_is_valid (xb_node_get_text (req))) { + const gchar *guid = xb_node_get_text (req); + const gchar *version; + g_autoptr(FuDevice) device2 = NULL; + + /* find if the other device exists */ + device2 = fu_device_list_get_by_guid (self->device_list, guid, error); + if (device2 == NULL) + return FALSE; + + /* get the version of the other device */ + version = fu_device_get_version (device2); + 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, + xb_node_get_attr (req, "version")); + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with %s: %s", + fu_device_get_name (device2), + error_local->message); + } + return FALSE; + } + return TRUE; + + } + /* not supported */ 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]); @@ -885,26 +1186,26 @@ 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 */ @@ -912,7 +1213,7 @@ 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; } @@ -920,8 +1221,9 @@ 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) { @@ -930,122 +1232,32 @@ } /* 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); - } + fu_idle_reset (self->idle); } -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; - - /* any quirks match */ - quirk = fu_quirks_lookup_by_glob (self->quirks, - FU_QUIRKS_DAEMON_VERSION_FORMAT, - as_app_get_id (app)); - if (g_strcmp0 (quirk, "none") == 0) - flags = AS_VERSION_PARSE_FLAG_NONE; - - /* 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 */ - if (g_str_has_prefix (version, "0x")) { - ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16); - } else { - ver_uint32 = g_ascii_strtoull (version, NULL, 10); - } - 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); -} - -static gchar * -fu_engine_get_boot_time (void) +static gchar * +fu_engine_get_boot_time (void) { g_autofree gchar *buf = NULL; g_auto(GStrv) lines = NULL; @@ -1173,7 +1385,13 @@ 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); @@ -1199,8 +1417,33 @@ } } + /* 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++) { + FuDevice *device; + g_autoptr(FuDevice) device_new = NULL; + g_autoptr(GError) error_local = NULL; + device = g_ptr_array_index (devices, i); + device_new = fu_device_list_get_by_id (self->device_list, + fu_device_get_id (device), + &error_local); + if (device_new == NULL) { + g_debug ("failed to find new device: %s", + error_local->message); + continue; + } + g_ptr_array_add (devices_new, g_steal_pointer (&device_new)); + } + /* notify the plugins about the composite action */ - if (!fu_engine_composite_cleanup (self, devices, error)) { + if (!fu_engine_composite_cleanup (self, devices_new, error)) { g_prefix_error (error, "failed to cleanup composite action: "); return FALSE; } @@ -1209,16 +1452,63 @@ return TRUE; } +static FwupdRelease * +fu_engine_create_release_metadata (FuEngine *self, FuPlugin *plugin, GError **error) +{ + const gchar *tmp; + g_autoptr(FwupdRelease) release = fwupd_release_new (); + g_autoptr(GHashTable) metadata_hash = NULL; + g_autoptr(GHashTable) os_release = NULL; + + /* add release data from os-release */ + os_release = fwupd_get_os_release (error); + if (os_release == NULL) + return NULL; + + /* build the version metadata */ + metadata_hash = fu_engine_get_report_metadata (self); + fwupd_release_add_metadata (release, metadata_hash); + fwupd_release_add_metadata (release, fu_plugin_get_report_metadata (plugin)); + + /* add details from os-release as metadata */ + tmp = g_hash_table_lookup (os_release, "ID"); + if (tmp != NULL) + fwupd_release_add_metadata_item (release, "DistroId", tmp); + tmp = g_hash_table_lookup (os_release, "VERSION_ID"); + if (tmp != NULL) + fwupd_release_add_metadata_item (release, "DistroVersion", tmp); + tmp = g_hash_table_lookup (os_release, "VARIANT_ID"); + if (tmp != NULL) + fwupd_release_add_metadata_item (release, "DistroVariant", tmp); + return g_steal_pointer (&release); +} + +static gboolean +fu_engine_is_running_offline (FuEngine *self) +{ +#ifdef HAVE_SYSTEMD + g_autofree gchar *default_target = NULL; + g_autoptr(GError) error = NULL; + default_target = fu_systemd_get_default_target (&error); + if (default_target == NULL) { + g_warning ("failed to get default.target: %s", error->message); + return FALSE; + } + return g_strcmp0 (default_target, "system-update.target") == 0; +#else + return FALSE; +#endif +} + /** * fu_engine_install: * @self: A #FuEngine - * @device: A #FuDevice - * @app: The #AsApp with the firmware metadata + * @task: A #FuInstallTask * @blob_cab: The #GBytes of the .cab file * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE * @error: A #GError, or %NULL * - * Installs a specfic firmware file on a device. + * Installs a specific firmware file on a device. * * By this point all the requirements and tests should have been done in * fu_engine_check_requirements() so this should not fail before running @@ -1233,27 +1523,31 @@ FwupdInstallFlags flags, GError **error) { - AsApp *app = fu_install_task_get_app (task); - AsChecksum *csum_tmp; - AsRelease *rel; - FuDevice *device = fu_install_task_get_device (task); + XbNode *component = fu_install_task_get_component (task); + FuPlugin *plugin; GBytes *blob_fw; - const gchar *tmp; - const gchar *version; + const gchar *tmp = NULL; + g_autofree gchar *release_key = NULL; + g_autofree gchar *version_orig = NULL; + g_autofree gchar *version_rel = NULL; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDevice) device_tmp = 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 */ + device = g_object_ref (fu_install_task_get_device (task)); 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, @@ -1271,40 +1565,36 @@ } /* 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); @@ -1314,10 +1604,141 @@ blob_fw2 = g_bytes_ref (blob_fw); } + /* get the plugin */ + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (device), + error); + if (plugin == NULL) + return FALSE; + + /* schedule this for the next reboot if not in system-update.target, + * but first check if allowed on battery power */ + version_rel = fu_engine_get_release_version (self, device, rel, error); + if (version_rel == NULL) { + g_prefix_error (error, "failed to get release version: "); + return FALSE; + } + if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) > 0 && + !fu_engine_is_running_offline (self)) { + g_autoptr(FwupdRelease) release_tmp = NULL; + plugin = fu_plugin_list_find_by_name (self->plugin_list, "upower", NULL); + if (plugin != NULL) { + if (!fu_plugin_runner_update_prepare (plugin, flags, device, error)) + return FALSE; + } + release_tmp = fu_engine_create_release_metadata (self, plugin, error); + if (release_tmp == NULL) + return FALSE; + fwupd_release_set_version (release_tmp, version_rel); + return fu_plugin_runner_schedule_update (plugin, device, release_tmp, + blob_cab, flags, error); + } + + /* add device to database */ + if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { + g_autoptr(FwupdRelease) release_tmp = NULL; + release_tmp = fu_engine_create_release_metadata (self, plugin, error); + if (release_tmp == NULL) + return FALSE; + tmp = xb_node_query_text (component, + "releases/release/checksum[@target='container']", + NULL); + if (tmp != NULL) + fwupd_release_add_checksum (release_tmp, tmp); + fwupd_release_set_version (release_tmp, version_rel); + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); + if (!fu_history_add_device (self->history, device, release_tmp, error)) + return FALSE; + } + /* install firmware blob */ - version = as_release_get_version (rel); - return fu_engine_install_blob (self, device, blob_cab, blob_fw2, - version, flags, error); + version_orig = g_strdup (fu_device_get_version (device)); + if (!fu_engine_install_blob (self, device, blob_fw2, flags, &error_local)) { + fu_device_set_status (device, FWUPD_STATUS_IDLE); + if (g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_AC_POWER_REQUIRED) || + g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW) || + g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_BROKEN_SYSTEM)) { + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + } else { + 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, + FU_HISTORY_FLAGS_MATCH_OLD_VERSION | + FU_HISTORY_FLAGS_MATCH_NEW_VERSION, + error)) { + return FALSE; + } + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + + /* the device may have changed */ + device_tmp = fu_device_list_get_by_id (self->device_list, + fu_device_get_id (device), + error); + if (device_tmp == NULL) { + g_prefix_error (error, "failed to get device after install: "); + return FALSE; + } + g_set_object (&device, device_tmp); + + /* update database */ + 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, + FU_HISTORY_FLAGS_MATCH_OLD_VERSION | + FU_HISTORY_FLAGS_MATCH_NEW_VERSION, + error)) + return FALSE; + /* success */ + return TRUE; + } + + /* for online updates, verify the version changed if not a re-install */ + if (version_rel != NULL && + g_strcmp0 (version_orig, version_rel) != 0 && + g_strcmp0 (version_orig, fu_device_get_version (device)) == 0) { + g_autofree gchar *str = NULL; + fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); + str = g_strdup_printf ("device version not updated on success, %s != %s", + version_rel, fu_device_get_version (device)); + fu_device_set_update_error (device, str); + if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && + !fu_history_modify_device (self->history, device, + FU_HISTORY_FLAGS_MATCH_OLD_VERSION, + error)) + return FALSE; + /* success */ + return TRUE; + } + + /* ensure the new version matched what we expected */ + if (version_rel != NULL && + g_strcmp0 (fu_device_get_version (device), version_rel) != 0) { + g_warning ("new device version '%s' was is not '%s', fixing up", + fu_device_get_version (device), version_rel); + fu_device_set_version (device, version_rel, + fu_device_get_version_format (device)); + } + + /* 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; + return TRUE; } /** @@ -1337,215 +1758,288 @@ return fu_plugin_list_get_all (self->plugin_list); } -gboolean -fu_engine_install_blob (FuEngine *self, - FuDevice *device, - GBytes *blob_cab, - GBytes *blob_fw2, - const gchar *version, - FwupdInstallFlags flags, - GError **error) +static FuDevice * +fu_engine_get_device_by_id (FuEngine *self, const gchar *device_id, GError **error) { - FuPlugin *plugin; - GPtrArray *plugins; - g_autofree gchar *device_id_orig = NULL; - g_autofree gchar *version_orig = NULL; - g_autoptr(FwupdRelease) release_history = fwupd_release_new (); - g_autoptr(GError) error_local = NULL; - g_autoptr(GHashTable) metadata_hash = NULL; + g_autoptr(FuDevice) device1 = NULL; + g_autoptr(FuDevice) device2 = NULL; - /* test the firmware is not an empty blob */ - if (g_bytes_get_size (blob_fw2) == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Firmware is invalid as has zero size"); + /* 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); +} + +static gboolean +fu_engine_update_prepare (FuEngine *self, + FwupdInstallFlags flags, + const gchar *device_id, + GError **error) +{ + GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list); + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device_by_id (self, device_id, error); + if (device == NULL) return FALSE; + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); + if (!fu_plugin_runner_update_prepare (plugin_tmp, flags, device, error)) + return FALSE; } + return TRUE; +} - /* we can only write history if we're providing a version number */ - if (version == NULL && (flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Version required if writing history"); +static gboolean +fu_engine_update_cleanup (FuEngine *self, + FwupdInstallFlags flags, + const gchar *device_id, + GError **error) +{ + GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list); + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device_by_id (self, device_id, error); + if (device == NULL) return FALSE; + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); + if (!fu_plugin_runner_update_cleanup (plugin_tmp, flags, device, error)) + return FALSE; } + return TRUE; +} - /* get the plugin */ +static gboolean +fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error) +{ + FuPlugin *plugin; + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device_by_id (self, device_id, error); + if (device == NULL) + return FALSE; plugin = fu_plugin_list_find_by_name (self->plugin_list, fu_device_get_plugin (device), error); if (plugin == NULL) return FALSE; + if (!fu_plugin_runner_update_detach (plugin, device, error)) + return FALSE; + return TRUE; +} - /* compare the versions of what we have installed */ - version_orig = g_strdup (fu_device_get_version (device)); +static gboolean +fu_engine_update_attach (FuEngine *self, const gchar *device_id, GError **error) +{ + FuPlugin *plugin; + g_autoptr(FuDevice) device = NULL; - /* signal to all the plugins the update is about to happen */ - plugins = fu_plugin_list_get_all (self->plugin_list); - for (guint j = 0; j < plugins->len; j++) { - FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); - if (!fu_plugin_runner_update_prepare (plugin_tmp, device, error)) - return FALSE; + /* the device and plugin both may have changed */ + device = fu_engine_get_device_by_id (self, device_id, error); + if (device == NULL) { + g_prefix_error (error, "failed to get device after update: "); + return FALSE; } + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (device), + error); + if (plugin == NULL) + return FALSE; + if (!fu_plugin_runner_update_attach (plugin, device, error)) + return FALSE; + return TRUE; +} - /* save the chosen device ID in case the device goes away */ - device_id_orig = g_strdup (fu_device_get_id (device)); +gboolean +fu_engine_activate (FuEngine *self, const gchar *device_id, GError **error) +{ + FuPlugin *plugin; + g_autoptr(FuDevice) device = NULL; - /* 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); + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* build the version metadata */ - metadata_hash = fu_engine_get_report_metadata (self); - fwupd_release_add_metadata (release_history, metadata_hash); - fwupd_release_add_metadata (release_history, - fu_plugin_get_report_metadata (plugin)); + /* check the device exists */ + device = fu_device_list_get_by_id (self->device_list, device_id, error); + if (device == NULL) + return FALSE; - /* add device to database */ - if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { - 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); - if (!fu_history_add_device (self->history, device, release_history, error)) - return FALSE; + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (device), + error); + if (plugin == NULL) + return FALSE; + g_debug ("Activating %s", fu_device_get_name (device)); + + if (!fu_plugin_runner_activate (plugin, device, error)) + return FALSE; + + fu_engine_emit_device_changed (self, device); + fu_engine_emit_changed (self); + + return TRUE; +} + +static gboolean +fu_engine_update_reload (FuEngine *self, const gchar *device_id, GError **error) +{ + FuPlugin *plugin; + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device_by_id (self, device_id, error); + if (device == NULL) { + g_prefix_error (error, "failed to get device after update: "); + return FALSE; + } + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (device), + error); + if (plugin == NULL) + return FALSE; + if (!fu_plugin_runner_update_reload (plugin, device, error)) { + g_prefix_error (error, "failed to reload device: "); + return FALSE; } + return TRUE; +} - /* do the update */ - if (!fu_plugin_runner_update_detach (plugin, device, &error_local)) { - fu_device_set_update_error (device, error_local->message); - if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && - !fu_history_modify_device (self->history, device, - FU_HISTORY_FLAGS_MATCH_OLD_VERSION, - error)) { - return FALSE; - } - g_propagate_error (error, g_steal_pointer (&error_local)); +static gboolean +fu_engine_update (FuEngine *self, + const gchar *device_id, + GBytes *blob_fw2, + FwupdInstallFlags flags, + GError **error) +{ + FuPlugin *plugin; + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device_by_id (self, device_id, error); + if (device == NULL) { + g_prefix_error (error, "failed to get device after detach: "); return FALSE; } - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (device), + error); + if (plugin == NULL) return FALSE; - if (!fu_plugin_runner_update (plugin, - device, - blob_cab, - blob_fw2, - flags, - &error_local)) { + if (!fu_plugin_runner_update (plugin, device, blob_fw2, flags, error)) { g_autoptr(GError) error_attach = NULL; + g_autoptr(GError) error_cleanup = NULL; - /* save to database */ - fu_device_set_update_error (device, error_local->message); - if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && - !fu_history_modify_device (self->history, device, - FU_HISTORY_FLAGS_MATCH_OLD_VERSION, - error)) { - return FALSE; - } - g_propagate_error (error, g_steal_pointer (&error_local)); - - /* attack back into runtime */ + /* attack back into runtime then cleanup */ if (!fu_plugin_runner_update_attach (plugin, device, &error_attach)) { g_warning ("failed to attach device after failed update: %s", error_attach->message); } - for (guint j = 0; j < plugins->len; j++) { - FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); - g_autoptr(GError) error_cleanup = NULL; - if (!fu_plugin_runner_update_cleanup (plugin_tmp, - device, - &error_cleanup)) { - g_warning ("failed to update-cleanup " - "after failed update: %s", - error_cleanup->message); - } + if (!fu_engine_update_cleanup (self, flags, device_id, &error_cleanup)) { + g_warning ("failed to update-cleanup after failed update: %s", + error_cleanup->message); } - fu_device_set_status (device, FWUPD_STATUS_IDLE); return FALSE; } - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) - return FALSE; - if (!fu_plugin_runner_update_attach (plugin, device, &error_local)) { - fu_device_set_update_error (device, error_local->message); - if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && - !fu_history_modify_device (self->history, device, - FU_HISTORY_FLAGS_MATCH_OLD_VERSION | - FU_HISTORY_FLAGS_MATCH_NEW_VERSION, - error)) { - return FALSE; - } - g_propagate_error (error, g_steal_pointer (&error_local)); + return TRUE; +} + +gboolean +fu_engine_install_blob (FuEngine *self, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + guint retries = 0; + g_autofree gchar *device_id = NULL; + g_autoptr(GTimer) timer = g_timer_new (); + + /* test the firmware is not an empty blob */ + if (g_bytes_get_size (blob_fw) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Firmware is invalid as has zero size"); return FALSE; } - /* get the new version number */ - device = fu_device_list_get_by_id (self->device_list, device_id_orig, error); - if (device == NULL) - return FALSE; - if (!fu_plugin_runner_update_reload (plugin, device, error)) - 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); - /* signal to all the plugins the update has happened */ - for (guint j = 0; j < plugins->len; j++) { - FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); - g_autoptr(GError) error_cleanup = NULL; - if (!fu_plugin_runner_update_cleanup (plugin_tmp, device, &error_cleanup)) { - g_warning ("failed to update-cleanup: %s", - error_cleanup->message); + /* plugins can set FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED to run again, but they + * must return TRUE rather than an error */ + device_id = g_strdup (fu_device_get_id (device)); + do { + /* check for a loop */ + if (++retries > 5) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "aborting device write loop, limit 5"); + return FALSE; } - } - /* make the UI update */ - fu_device_set_status (device, FWUPD_STATUS_IDLE); - fu_engine_emit_device_changed (self, device); - fu_engine_emit_changed (self); + /* don't rely on a plugin clearing this */ + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); - /* update database */ - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) { - 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, - FU_HISTORY_FLAGS_MATCH_OLD_VERSION, - error)) + /* signal to all the plugins the update is about to happen */ + if (!fu_engine_update_prepare (self, flags, device_id, error)) return FALSE; - /* success */ - return TRUE; - } - /* for online updates, verify the version changed if not a re-install */ - if (version != NULL && - g_strcmp0 (version_orig, version) != 0 && - g_strcmp0 (version_orig, fu_device_get_version (device)) == 0) { - 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, - FU_HISTORY_FLAGS_MATCH_OLD_VERSION, - error)) + /* detach to bootloader mode */ + if (!fu_engine_update_detach (self, device_id, error)) return FALSE; - /* success */ - return TRUE; - } - /* ensure the new version matched what we expected */ - if (version != NULL && - g_strcmp0 (fu_device_get_version (device), version) != 0) { - g_warning ("new device version '%s' was is not '%s', fixing up", - fu_device_get_version (device), version); - fu_device_set_version (device, version); - } + /* install */ + if (!fu_engine_update (self, device_id, blob_fw, flags, error)) + return FALSE; - /* success */ - if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && - !fu_history_modify_device (self->history, device, - FU_HISTORY_FLAGS_MATCH_NEW_VERSION, - error)) + /* attach into runtime mode */ + if (!fu_engine_update_attach (self, device_id, error)) + return FALSE; + + } while (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED)); + + /* get the new version number */ + if (!fu_engine_update_reload (self, device_id, error)) return FALSE; - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS); + + /* signal to all the plugins the update has happened */ + if (!fu_engine_update_cleanup (self, flags, device_id, error)) + return FALSE; + + /* make the UI update */ + fu_engine_set_status (self, FWUPD_STATUS_IDLE); + fu_engine_emit_changed (self); + g_debug ("Updating %s took %f seconds", fu_device_get_name (device), + g_timer_elapsed (timer, NULL)); return TRUE; } @@ -1572,6 +2066,7 @@ /* only useful */ if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS || + fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT || fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) { return g_steal_pointer (&dev); } @@ -1592,6 +2087,7 @@ for (guint i = 0; i < devices->len; i++) { FuDevice *dev = g_ptr_array_index (devices, i); if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS || + fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT || fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) return g_object_ref (dev); } @@ -1602,118 +2098,160 @@ return NULL; } -static void -fu_engine_add_component_to_store (FuEngine *self, AsApp *app) +/* for the self tests */ +void +fu_engine_set_silo (FuEngine *self, XbSilo *silo) { - AsApp *app_old = as_store_get_app_by_id (self->store, as_app_get_id (app)); - GPtrArray *releases = as_app_get_releases (app); - - /* 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); - - /* the app does not already exist */ - if (app_old == NULL) { - as_store_add_app (self->store, app); - return; - } - - /* add releases that do not exist from a higher priority remote */ - 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) { - g_debug ("skipping release %s that already exists for %s", - version, as_app_get_id (app_old)); - continue; - } - g_debug ("adding release %s to existing %s", - version, as_app_get_id (app_old)); - as_app_add_release (app_old, release); - } + 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_load_metadata_from_file (FuEngine *self, - const gchar *path, - const gchar *remote_id, - GError **error) +fu_engine_is_device_supported (FuEngine *self, FuDevice *device) { - GPtrArray *apps; - g_autoptr(AsStore) store = NULL; - g_autoptr(GFile) file = NULL; - g_autoptr(GBytes) remote_blob = NULL; + g_autoptr(XbNode) component = 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)) + /* sanity check */ + if (self->silo == NULL) { + g_critical ("FuEngine silo not set up"); 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); + /* no device version */ + if (fu_device_get_version (device) == NULL) + return FALSE; - /* add the new application from the store */ - apps = as_store_get_apps (store); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); + /* match the GUIDs in the XML */ + component = fu_engine_get_component_by_guids (self, device); + if (component == NULL) + return FALSE; - /* 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); - } - } + /* success */ + return TRUE; +} - /* either add component, or merge in new releases */ - fu_engine_add_component_to_store (self, app); - } +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_is_device_supported (FuEngine *self, FuDevice *device) +fu_engine_create_metadata (FuEngine *self, XbBuilder *builder, + FwupdRemote *remote, GError **error) { - AsApp *app; + g_autoptr(GPtrArray) files = NULL; + const gchar *path; - /* no device version */ - if (fu_device_get_version (device) == NULL) - return FALSE; + /* 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); - /* match the GUIDs in the XML */ - app = fu_engine_store_get_app_by_guids (self->store, device); - if (app == NULL) - return FALSE; + /* check is cab file */ + if (!g_str_has_suffix (fn, ".cab")) { + g_debug ("ignoring: %s", fn); + continue; + } - /* success */ + /* 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) +fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError **error) { - GPtrArray *apps; GPtrArray *remotes; - g_autofree gchar *guids_str = NULL; + XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID; + 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 silo */ + g_clear_object (&self->silo); - /* clear existing store */ - as_store_remove_all (self->store); + /* 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", @@ -1725,55 +2263,81 @@ 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; } - } - /* print what we've got */ - apps = as_store_get_apps (self->store); - if (apps->len == 0) { - g_debug ("no devices in store"); - } else { - g_debug ("devices now in store:"); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); - GPtrArray *releases = as_app_get_releases (app); - g_autoptr(GString) releases_str = g_string_new (NULL); - - for (guint j = 0; j < releases->len; j++) { - AsRelease *release = g_ptr_array_index (releases, j); - g_string_append_printf (releases_str, "%s,", - as_release_get_version (release)); - if (j >= 2) { - g_string_append (releases_str, "…,"); - break; - } - } - if (releases_str->len > 1) - g_string_truncate (releases_str, releases_str->len - 1); - g_debug ("%u\t%s\t%s [%s]", i + 1, - as_app_get_id (app), - as_app_get_name (app, NULL), - releases_str->str); - } - } + /* 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); + } + +#if LIBXMLB_CHECK_VERSION(0,1,7) + /* on a read-only filesystem don't care about the cache GUID */ + if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS) + compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; +#endif - /* 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])); - } - } + /* 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, compile_flags, NULL, error); + if (self->silo == NULL) + return FALSE; + + /* print what we've got */ + components = xb_silo_query (self->silo, "components/component", 0, NULL); + if (components != NULL) + g_debug ("%u components now in silo", components->len); + + /* 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); @@ -1811,7 +2375,8 @@ blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error); if (blob_sig == NULL) return NULL; - return fu_keyring_verify_data (kr, blob, blob_sig, error); + return fu_keyring_verify_data (kr, blob, blob_sig, + FU_KEYRING_VERIFY_FLAG_NONE, error); } /** @@ -1894,7 +2459,9 @@ pki_dir = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL); if (!fu_keyring_add_public_keys (kr, pki_dir, error)) return FALSE; - kr_result = fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error); + kr_result = fu_keyring_verify_data (kr, bytes_raw, bytes_sig, + FU_KEYRING_VERIFY_FLAG_NONE, + error); if (kr_result == NULL) return FALSE; @@ -1943,25 +2510,23 @@ bytes_sig, error)) return FALSE; } - return fu_engine_load_metadata_store (self, error); + return fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE, error); } /** - * 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); @@ -1969,68 +2534,60 @@ /* 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) +static FuDevice * +fu_engine_get_result_from_component (FuEngine *self, XbNode *component, GError **error) { - FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE; - AsRelease *release; - GPtrArray *provides; + FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE; g_autoptr(FuInstallTask) task = NULL; - g_autoptr(FwupdDevice) dev = NULL; + g_autoptr(FuDevice) dev = NULL; g_autoptr(FwupdRelease) rel = NULL; - - dev = fwupd_device_new (); - provides = as_app_get_provides (app); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) provides = NULL; + g_autoptr(XbNode) description = NULL; + g_autoptr(XbNode) release = NULL; + + dev = fu_device_new (); + 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)); - FuDevice *device; + XbNode *prov = XB_NODE (g_ptr_array_index (provides, i)); const gchar *guid; - - /* not firmware */ - if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; + g_autoptr(FuDevice) device = NULL; /* 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); if (device != NULL) { - fwupd_device_set_name (dev, fu_device_get_name (device)); - fwupd_device_set_flags (dev, fu_device_get_flags (device)); - fwupd_device_set_id (dev, fu_device_get_id (device)); + fu_device_set_name (dev, fu_device_get_name (device)); + fu_device_set_flags (dev, fu_device_get_flags (device)); + fu_device_set_id (dev, fu_device_get_id (device)); } /* add GUID */ - fwupd_device_add_guid (dev, guid); + fu_device_add_guid (dev, guid); } - if (fwupd_device_get_guids(dev)->len == 0) { + if (fu_device_get_guids(dev)->len == 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -2038,24 +2595,58 @@ return NULL; } + /* get (or guess) the component version format */ + if (!fu_engine_set_device_version_format (self, dev, component, error)) + return NULL; + /* 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); - if (!fu_keyring_get_release_trust_flags (release, &trust_flags, error)) + 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_flags (release, + &release_flags, + &error_local)) { + if (g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning ("Ignoring verification: %s", + error_local->message); + } else { + g_propagate_error (error, g_steal_pointer (&error_local)); + return NULL; + } + } /* 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) + fu_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); - fwupd_device_add_release (dev, rel); + fwupd_release_set_flags (rel, release_flags); + if (!fu_engine_set_release_from_appstream (self, dev, rel, component, release, error)) + return NULL; + fu_device_add_release (dev, rel); return g_steal_pointer (&dev); } @@ -2069,64 +2660,64 @@ * * Note: this will close the fd when done * - * Returns: (transfer container) (element-type FwupdDevice): results + * Returns: (transfer container) (element-type FuDevice): results **/ 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 NULL; + if (!xb_silo_query_build_index (silo, "components/component/provides/firmware", + NULL, error)) + return NULL; + /* 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); - FwupdDevice *dev; - g_autoptr(FuInstallTask) task = NULL; - - /* check we can install it */ - task = fu_install_task_new (NULL, app); - if (!fu_engine_check_requirements (self, task, - FWUPD_INSTALL_FLAG_NONE, - error)) - return NULL; - - as_app_set_origin (app, as_store_get_origin (store)); - dev = fu_engine_get_result_from_app (self, app, error); + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); + FuDevice *dev; + 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); + FwupdRelease *rel = fu_device_get_release_default (dev); fwupd_release_set_remote_id (rel, remote_id); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED); } @@ -2135,6 +2726,21 @@ return g_steal_pointer (&details); } +static gint +fu_engine_sort_devices_by_priority (gconstpointer a, gconstpointer b) +{ + FuDevice *dev_a = *((FuDevice **) a); + FuDevice *dev_b = *((FuDevice **) b); + gint prio_a = fu_device_get_priority (dev_a); + gint prio_b = fu_device_get_priority (dev_b); + + if (prio_a > prio_b) + return -1; + if (prio_a < prio_b) + return 1; + return 0; +} + /** * fu_engine_get_devices: * @self: A #FuEngine @@ -2160,6 +2766,7 @@ "No detected devices"); return NULL; } + g_ptr_array_sort (devices, fu_engine_sort_devices_by_priority); return g_steal_pointer (&devices); } @@ -2177,10 +2784,11 @@ fu_engine_get_device (FuEngine *self, const gchar *device_id, GError **error) { FuDevice *device; + device = fu_device_list_get_by_id (self->device_list, device_id, error); if (device == NULL) return NULL; - return g_object_ref (device); + return device; } /** @@ -2266,44 +2874,158 @@ 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_check_release_is_approved (FuEngine *self, FwupdRelease *rel) { - g_autoptr(GError) error_all = NULL; + GPtrArray *csums = fwupd_release_get_checksums (rel); + for (guint i = 0; i < csums->len; i++) { + const gchar *csum = g_ptr_array_index (csums, i); + g_debug ("checking %s against approved list", csum); + if (g_hash_table_lookup (self->approved_firmware, csum) != NULL) + return TRUE; + } + return FALSE; +} - /* 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); +static gboolean +fu_engine_add_releases_for_device_component (FuEngine *self, + FuDevice *device, + XbNode *component, + GPtrArray *releases, + GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(FuInstallTask) task = fu_install_task_new (device, component); + g_autoptr(GPtrArray) releases_tmp = NULL; + + if (!fu_engine_check_requirements (self, task, + FWUPD_INSTALL_FLAG_OFFLINE | + 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 *remote_id; + const gchar *update_message; + gint vercmp; + GPtrArray *checksums; + g_autoptr(FwupdRelease) rel = fwupd_release_new (); + g_autoptr(GError) error_loop = NULL; + + /* create new FwupdRelease for the XbNode */ + if (!fu_engine_set_release_from_appstream (self, + device, + rel, + component, + release, + &error_loop)) { + g_warning ("failed to set release for component: %s", + error_loop->message); continue; } - return g_object_ref (app_tmp); + + /* 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; + + /* test for upgrade or downgrade */ + vercmp = fu_common_vercmp (fwupd_release_get_version (rel), + fu_device_get_version (device)); + if (vercmp > 0) + fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_UPGRADE); + else if (vercmp < 0) + fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_DOWNGRADE); + + /* lower than allowed to downgrade to */ + if (fu_device_get_version_lowest (device) != NULL && + fu_common_vercmp (fwupd_release_get_version (rel), + fu_device_get_version_lowest (device)) < 0) { + fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_VERSION); + } + + /* check if remote is whitelisting firmware */ + remote_id = fwupd_release_get_remote_id (rel); + if (remote_id != NULL) { + FwupdRemote *remote = fu_engine_get_remote_by_id (self, remote_id, NULL); + if (remote != NULL && + fwupd_remote_get_approval_required (remote) && + !fu_engine_check_release_is_approved (self, rel)) { + fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); + } + } + + /* 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); + } + /* 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 * @@ -2312,6 +3034,10 @@ 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); @@ -2334,46 +3060,62 @@ 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; + } + g_propagate_error (error, g_steal_pointer (&error_local)); + 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) + /* 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); + } + } - /* success */ - g_ptr_array_add (releases, g_steal_pointer (&rel)); + /* return the compound error */ + if (releases->len == 0) { + if (error_all != NULL) { + g_propagate_prefixed_error (error, g_steal_pointer (&error_all), + "No releases found for device: "); + return NULL; } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No releases found for device"); + return NULL; } return releases; } @@ -2391,7 +3133,7 @@ GPtrArray * fu_engine_get_releases (FuEngine *self, const gchar *device_id, GError **error) { - FuDevice *device; + g_autoptr(FuDevice) device = NULL; g_autoptr(GPtrArray) releases = NULL; g_return_val_if_fail (FU_IS_ENGINE (self), NULL); @@ -2431,7 +3173,7 @@ GPtrArray * fu_engine_get_downgrades (FuEngine *self, const gchar *device_id, GError **error) { - FuDevice *device; + g_autoptr(FuDevice) device = NULL; g_autoptr(GPtrArray) releases = NULL; g_autoptr(GPtrArray) releases_tmp = NULL; g_autoptr(GString) error_str = g_string_new (NULL); @@ -2452,12 +3194,10 @@ releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < releases_tmp->len; i++) { FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i); - gint vercmp; - /* only include older firmware */ - vercmp = as_utils_vercmp (fwupd_release_get_version (rel_tmp), - fu_device_get_version (device)); - if (vercmp == 0) { + /* same as installed */ + if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) && + !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) { g_string_append_printf (error_str, "%s=same, ", fwupd_release_get_version (rel_tmp)); g_debug ("ignoring %s as the same as %s", @@ -2465,7 +3205,9 @@ fu_device_get_version (device)); continue; } - if (vercmp > 0) { + + /* newer than current */ + if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE)) { g_string_append_printf (error_str, "%s=newer, ", fwupd_release_get_version (rel_tmp)); g_debug ("ignoring %s as newer than %s", @@ -2474,17 +3216,14 @@ continue; } - /* 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), - fu_device_get_version_lowest (device)) <= 0) { - g_string_append_printf (error_str, "%s=lowest, ", - fwupd_release_get_version (rel_tmp)); - g_debug ("ignoring %s as older than lowest %s", - fwupd_release_get_version (rel_tmp), - fu_device_get_version_lowest (device)); - continue; - } + /* don't show releases we are not allowed to downgrade to */ + if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_VERSION)) { + g_string_append_printf (error_str, "%s=lowest, ", + fwupd_release_get_version (rel_tmp)); + g_debug ("ignoring %s as older than lowest %s", + fwupd_release_get_version (rel_tmp), + fu_device_get_version_lowest (device)); + continue; } g_ptr_array_add (releases, g_object_ref (rel_tmp)); } @@ -2511,6 +3250,54 @@ return g_steal_pointer (&releases); } +GPtrArray * +fu_engine_get_approved_firmware (FuEngine *self) +{ + GPtrArray *checksums = g_ptr_array_new_with_free_func (g_free); + g_autoptr(GList) keys = g_hash_table_get_keys (self->approved_firmware); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *csum = l->data; + g_ptr_array_add (checksums, g_strdup (csum)); + } + return checksums; +} + +void +fu_engine_add_approved_firmware (FuEngine *self, const gchar *checksum) +{ + g_hash_table_add (self->approved_firmware, g_strdup (checksum)); +} + +gchar * +fu_engine_self_sign (FuEngine *self, + const gchar *value, + FuKeyringSignFlags flags, + GError **error) +{ + g_autoptr(FuKeyring) kr = NULL; + g_autoptr(FuKeyringResult) kr_result = NULL; + g_autoptr(GBytes) payload = NULL; + g_autoptr(GBytes) signature = NULL; + + /* create detached signature and verify */ + kr = fu_keyring_create_for_kind (FWUPD_KEYRING_KIND_PKCS7, error); + if (kr == NULL) + return NULL; + if (!fu_keyring_setup (kr, error)) + return NULL; + payload = g_bytes_new (value, strlen (value)); + signature = fu_keyring_sign_data (kr, payload, flags, error); + if (signature == NULL) + return NULL; + kr_result = fu_keyring_verify_data (kr, payload, signature, + FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT, + error); + if (kr_result == NULL) + return NULL; + return g_strndup (g_bytes_get_data (signature, NULL), + g_bytes_get_size (signature)); +} + /** * fu_engine_get_upgrades: * @self: A #FuEngine @@ -2524,7 +3311,7 @@ GPtrArray * fu_engine_get_upgrades (FuEngine *self, const gchar *device_id, GError **error) { - FuDevice *device; + g_autoptr(FuDevice) device = NULL; g_autoptr(GPtrArray) releases = NULL; g_autoptr(GPtrArray) releases_tmp = NULL; g_autoptr(GString) error_str = g_string_new (NULL); @@ -2538,6 +3325,16 @@ if (device == NULL) return NULL; + /* don't show upgrades again until we reboot */ + if (fu_device_get_update_state (device) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No upgrades for %s: A reboot is pending", + fu_device_get_name (device)); + return NULL; + } + /* get all the releases for the device */ releases_tmp = fu_engine_get_releases_for_device (self, device, error); if (releases_tmp == NULL) @@ -2545,12 +3342,10 @@ releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < releases_tmp->len; i++) { FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i); - gint vercmp; - /* only include older firmware */ - vercmp = as_utils_vercmp (fwupd_release_get_version (rel_tmp), - fu_device_get_version (device)); - if (vercmp == 0) { + /* same as installed */ + if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) && + !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) { g_string_append_printf (error_str, "%s=same, ", fwupd_release_get_version (rel_tmp)); g_debug ("ignoring %s as the same as %s", @@ -2558,7 +3353,9 @@ fu_device_get_version (device)); continue; } - if (vercmp < 0) { + + /* older than current */ + if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) { g_string_append_printf (error_str, "%s=older, ", fwupd_release_get_version (rel_tmp)); g_debug ("ignoring %s as older than %s", @@ -2566,6 +3363,17 @@ fu_device_get_version (device)); continue; } + + /* not approved */ + if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL)) { + g_string_append_printf (error_str, "%s=not-approved, ", + fwupd_release_get_version (rel_tmp)); + g_debug ("ignoring %s as not approved as required by %s", + fwupd_release_get_version (rel_tmp), + fwupd_release_get_remote_id (rel_tmp)); + continue; + } + g_ptr_array_add (releases, g_object_ref (rel_tmp)); } if (error_str->len > 2) @@ -2686,20 +3494,10 @@ static void fu_engine_plugins_setup (FuEngine *self) { - GPtrArray *plugins; - g_autoptr(AsProfileTask) ptask = NULL; - - ptask = as_profile_start_literal (self->profile, "FuEngine:setup"); - g_assert (ptask != NULL); - plugins = fu_plugin_list_get_all (self->plugin_list); + GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list); for (guint i = 0; i < plugins->len; i++) { g_autoptr(GError) error = NULL; - g_autoptr(AsProfileTask) ptask2 = NULL; FuPlugin *plugin = g_ptr_array_index (plugins, i); - ptask2 = as_profile_start (self->profile, - "FuEngine:setup{%s}", - fu_plugin_get_name (plugin)); - g_assert (ptask2 != NULL); if (!fu_plugin_runner_startup (plugin, &error)) { fu_plugin_set_enabled (plugin, FALSE); g_message ("disabling plugin because: %s", error->message); @@ -2711,7 +3509,6 @@ fu_engine_plugins_coldplug (FuEngine *self, gboolean is_recoldplug) { GPtrArray *plugins; - g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(GString) str = g_string_new (NULL); /* don't allow coldplug to be scheduled when in coldplug */ @@ -2733,16 +3530,9 @@ } /* exec */ - ptask = as_profile_start_literal (self->profile, "FuEngine:coldplug"); - g_assert (ptask != NULL); for (guint i = 0; i < plugins->len; i++) { g_autoptr(GError) error = NULL; - g_autoptr(AsProfileTask) ptask2 = NULL; FuPlugin *plugin = g_ptr_array_index (plugins, i); - ptask2 = as_profile_start (self->profile, - "FuEngine:coldplug{%s}", - fu_plugin_get_name (plugin)); - g_assert (ptask2 != NULL); if (is_recoldplug) { if (!fu_plugin_runner_recoldplug (plugin, &error)) g_message ("failed recoldplug: %s", error->message); @@ -2772,7 +3562,7 @@ } if (str->len > 2) { g_string_truncate (str, str->len - 2); - g_message ("using plugins: %s", str->str); + g_debug ("using plugins: %s", str->str); } /* we can recoldplug from this point on */ @@ -2811,6 +3601,16 @@ gpointer user_data) { FuEngine *self = (FuEngine *) user_data; + gint priority = fu_plugin_get_priority (plugin); + GPtrArray *children = fu_device_get_children (device); + /* set the priority to 1 greater than biggest child */ + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + gint child_priority = fu_device_get_priority (child); + if (child_priority >= priority) + priority = child_priority + 1; + } + fu_device_set_priority (device, priority); fu_engine_add_device (self, device); } @@ -2860,6 +3660,34 @@ } } +static void +fu_engine_device_inherit_history (FuEngine *self, FuDevice *device) +{ + g_autoptr(FuDevice) device_history = NULL; + + /* any success or failed update? */ + device_history = fu_history_get_device_by_id (self->history, + fu_device_get_id (device), + NULL); + if (device_history == NULL) + return; + + /* the device is still running the old firmware version and so if it + * required activation before, it still requires it now -- note: + * we can't just check for version_new=version to allow for re-installs */ + if (fu_device_has_flag (device_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + FwupdRelease *release = fu_device_get_release_default (device_history); + if (fu_common_vercmp (fu_device_get_version (device), + fwupd_release_get_version (release)) != 0) { + g_debug ("inheriting needs-activation for %s as version %s != %s", + fu_device_get_name (device), + fu_device_get_version (device), + fwupd_release_get_version (release)); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + } + } +} + void fu_engine_add_device (FuEngine *self, FuDevice *device) { @@ -2892,20 +3720,76 @@ } } + /* if this device is locked get some metadata from AppStream */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) { + 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 (); + g_autoptr(GError) error_local = NULL; + if (!fu_engine_set_release_from_appstream (self, + device, + rel, + component, + release, + &error_local)) { + g_warning ("failed to set AppStream release: %s", + error_local->message); + } else { + fu_device_add_release (device, rel); + } + } + } + } + /* adopt any required children, which may or may not already exist */ fu_engine_adopt_children (self, device); + /* set any alternate objects on the device from the ID */ + if (fu_device_get_alternate_id (device) != NULL) { + g_autoptr(FuDevice) device_alt = NULL; + device_alt = fu_device_list_get_by_id (self->device_list, + fu_device_get_alternate_id (device), + NULL); + if (device_alt != NULL) + fu_device_set_alternate (device, device_alt); + } + + if (fu_device_get_version_format (device) == FWUPD_VERSION_FORMAT_UNKNOWN && + fu_common_version_guess_format (fu_device_get_version (device)) == FWUPD_VERSION_FORMAT_NUMBER) { + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_update_error (device, "VersionFormat is ambiguous for this device"); + } + /* notify all plugins about this new 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); + /* sometimes inherit flags from recent history */ + fu_engine_device_inherit_history (self, 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 @@ -2914,8 +3798,8 @@ gpointer user_data) { FuEngine *self = (FuEngine *) user_data; - FuDevice *device_tmp; FuPlugin *plugin_old; + g_autoptr(FuDevice) device_tmp = NULL; g_autoptr(GError) error = NULL; device_tmp = fu_device_list_get_by_id (self->device_list, @@ -2959,6 +3843,128 @@ } static void +fu_engine_udev_device_add (FuEngine *self, GUdevDevice *udev_device) +{ + GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list); + const gchar *plugin_name; + g_autoptr(FuUdevDevice) device = fu_udev_device_new (udev_device); + g_autoptr(GError) error_local = NULL; + + /* add any extra quirks */ + fu_device_set_quirks (FU_DEVICE (device), self->quirks); + if (!fu_device_probe (FU_DEVICE (device), &error_local)) { + g_warning ("failed to probe device %s: %s", + g_udev_device_get_sysfs_path (udev_device), + error_local->message); + return; + } + + /* can be specified using a quirk */ + plugin_name = fu_device_get_plugin (FU_DEVICE (device)); + if (plugin_name != NULL) { + g_autoptr(GError) error = NULL; + FuPlugin *plugin = fu_plugin_list_find_by_name (self->plugin_list, + plugin_name, &error); + if (plugin == NULL) { + g_warning ("failed to find specified plugin %s: %s", + plugin_name, error->message); + return; + } + if (!fu_plugin_runner_udev_device_added (plugin, device, &error)) { + g_warning ("failed to add udev device %s: %s", + g_udev_device_get_sysfs_path (udev_device), + error->message); + } + return; + } + + /* call into each plugin */ + g_debug ("no plugin specified for udev device %s", + g_udev_device_get_sysfs_path (udev_device)); + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); + g_autoptr(GError) error = NULL; + + /* skipping plugin as requires quirk */ + if (fu_plugin_has_rule (plugin_tmp, + FU_PLUGIN_RULE_REQUIRES_QUIRK, + FU_QUIRKS_PLUGIN)) { + continue; + } + + /* run all plugins */ + if (!fu_plugin_runner_udev_device_added (plugin_tmp, device, &error)) { + if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug ("%s ignoring: %s", + fu_plugin_get_name (plugin_tmp), + error->message); + continue; + } + g_warning ("%s failed to add udev device %s: %s", + fu_plugin_get_name (plugin_tmp), + g_udev_device_get_sysfs_path (udev_device), + error->message); + } + } +} + +static void +fu_engine_udev_device_remove (FuEngine *self, GUdevDevice *udev_device) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* go through each device and remove any that match */ + devices = fu_device_list_get_all (self->device_list); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + if (!FU_IS_UDEV_DEVICE (device)) + continue; + if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)), + g_udev_device_get_sysfs_path (udev_device)) == 0) { + g_debug ("auto-removing GUdevDevice"); + fu_device_list_remove (self->device_list, device); + } + } +} + +static void +fu_engine_udev_device_changed (FuEngine *self, GUdevDevice *udev_device) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* emit changed on any that match */ + devices = fu_device_list_get_all (self->device_list); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + if (!FU_IS_UDEV_DEVICE (device)) + continue; + if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)), + g_udev_device_get_sysfs_path (udev_device)) == 0) { + fu_udev_device_emit_changed (FU_UDEV_DEVICE (device)); + } + } +} + +static void +fu_engine_enumerate_udev (FuEngine *self) +{ + /* get all devices of class */ + for (guint i = 0; i < self->udev_subsystems->len; i++) { + const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i); + GList *devices = g_udev_client_query_by_subsystem (self->gudev_client, + subsystem); + g_debug ("%u devices with subsystem %s", + g_list_length (devices), subsystem); + for (GList *l = devices; l != NULL; l = l->next) { + GUdevDevice *udev_device = l->data; + fu_engine_udev_device_add (self, udev_device); + } + g_list_foreach (devices, (GFunc) g_object_unref, NULL); + g_list_free (devices); + } +} + +static void fu_engine_plugin_recoldplug_cb (FuPlugin *plugin, FuEngine *self) { if (self->coldplug_running) { @@ -2968,6 +3974,7 @@ if (self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) { g_debug ("doing direct recoldplug"); fu_engine_plugins_coldplug (self, TRUE); + fu_engine_enumerate_udev (self); return; } g_debug ("scheduling a recoldplug"); @@ -2984,10 +3991,22 @@ 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); } @@ -3024,18 +4043,31 @@ 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) { const gchar *fn; g_autoptr(GDir) dir = NULL; - g_autoptr(AsProfileTask) ptask = NULL; g_autofree gchar *plugin_path = NULL; - /* profile */ - ptask = as_profile_start_literal (self->profile, "FuEngine:load-plugins"); - g_assert (ptask != NULL); - /* search */ plugin_path = fu_common_get_path (FU_PATH_KIND_PLUGINDIR_PKG); dir = g_dir_open (plugin_path, 0, error); @@ -3071,7 +4103,7 @@ 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); fu_plugin_set_compile_versions (plugin, self->compile_versions); @@ -3109,9 +4141,15 @@ 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 */ @@ -3122,38 +4160,6 @@ return TRUE; } -/** - * fu_engine_check_plugins_pending: - * @self: A #FuEngine - * @error: A #GError, or %NULL - * - * Checks if any plugins have pending devices to be added. - * - * Returns: %FALSE if any plugins have pending devices. - **/ -gboolean -fu_engine_check_plugins_pending (FuEngine *self, GError **error) -{ - GPtrArray *plugins; - - g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - plugins = fu_plugin_list_get_all (self->plugin_list); - for (guint i = 0; i < plugins->len; i++) { - FuPlugin *plugin = g_ptr_array_index (plugins, i); - if (fu_plugin_has_device_delay (plugin)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "%s pending", - fu_plugin_get_name (plugin)); - return FALSE; - } - } - return TRUE; -} - static gboolean fu_engine_cleanup_state (GError **error) { @@ -3190,9 +4196,9 @@ FuDevice *device = g_ptr_array_index (devices, i); if (!FU_IS_USB_DEVICE (device)) continue; - if (g_strcmp0 (fu_device_get_platform_id (device), + if (g_strcmp0 (fu_usb_device_get_platform_id (FU_USB_DEVICE (device)), g_usb_device_get_platform_id (usb_device)) == 0) { - g_debug ("auto-removing FuUsbDevice"); + g_debug ("auto-removing GUsbDevice"); fu_device_list_remove (self->device_list, device); } } @@ -3204,17 +4210,64 @@ FuEngine *self) { GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list); + const gchar *plugin_name; + g_autoptr(FuUsbDevice) device = fu_usb_device_new (usb_device); + g_autoptr(GError) error_local = NULL; + + /* add any extra quirks */ + fu_device_set_quirks (FU_DEVICE (device), self->quirks); + if (!fu_device_probe (FU_DEVICE (device), &error_local)) { + g_warning ("failed to probe device %s: %s", + fu_device_get_physical_id (FU_DEVICE (device)), + error_local->message); + return; + } + + /* can be specified using a quirk */ + plugin_name = fu_device_get_plugin (device); + if (plugin_name != NULL) { + g_autoptr(GError) error = NULL; + FuPlugin *plugin = fu_plugin_list_find_by_name (self->plugin_list, + plugin_name, &error); + if (plugin == NULL) { + g_warning ("failed to find specified plugin %s: %s", + plugin_name, error->message); + return; + } + if (!fu_plugin_runner_usb_device_added (plugin, device, &error)) { + g_warning ("failed to add USB device %04x:%04x: %s", + g_usb_device_get_vid (usb_device), + g_usb_device_get_pid (usb_device), + error->message); + } + return; + } /* call into each plugin */ + g_debug ("no plugin specified for USB device %04x:%04x", + g_usb_device_get_vid (usb_device), + g_usb_device_get_pid (usb_device)); for (guint j = 0; j < plugins->len; j++) { FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); g_autoptr(GError) error = NULL; - if (!fu_plugin_runner_usb_device_added (plugin_tmp, usb_device, &error)) { + + /* skipping plugin as requires quirk */ + if (fu_plugin_has_rule (plugin_tmp, + FU_PLUGIN_RULE_REQUIRES_QUIRK, + FU_QUIRKS_PLUGIN)) { + continue; + } + + /* create a device, then probe */ + if (!fu_plugin_runner_usb_device_added (plugin_tmp, device, &error)) { if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { - g_debug ("ignoring: %s", error->message); + g_debug ("%s ignoring: %s", + fu_plugin_get_name (plugin_tmp), + error->message); continue; } - g_warning ("failed to add USB device %04x:%04x: %s", + g_warning ("%s failed to add USB device %04x:%04x: %s", + fu_plugin_get_name (plugin_tmp), g_usb_device_get_vid (usb_device), g_usb_device_get_pid (usb_device), error->message); @@ -3226,12 +4279,7 @@ static void fu_engine_load_quirks (FuEngine *self) { - g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(GError) error = NULL; - - /* profile */ - ptask = as_profile_start_literal (self->profile, "FuEngine:load-quirks"); - g_assert (ptask != NULL); if (!fu_quirks_load (self->quirks, &error)) g_warning ("Failed to load quirks: %s", error->message); } @@ -3239,12 +4287,7 @@ static void fu_engine_load_smbios (FuEngine *self) { - g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(GError) error = NULL; - - /* profile */ - ptask = as_profile_start_literal (self->profile, "FuEngine:load-smbios"); - g_assert (ptask != NULL); if (!fu_smbios_setup (self->smbios, &error)) g_warning ("Failed to load SMBIOS: %s", error->message); } @@ -3252,12 +4295,7 @@ static void fu_engine_load_hwids (FuEngine *self) { - g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(GError) error = NULL; - - /* profile */ - ptask = as_profile_start_literal (self->profile, "FuEngine:load-hwids"); - g_assert (ptask != NULL); if (!fu_hwids_setup (self->hwids, self->smbios, &error)) g_warning ("Failed to load HWIDs: %s", error->message); } @@ -3265,10 +4303,10 @@ static gboolean fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError **error) { - FuDevice *dev; FuPlugin *plugin; FwupdRelease *rel_history; g_autofree gchar *btime = NULL; + g_autoptr(FuDevice) dev = NULL; /* is in the device list */ dev = fu_device_list_get_by_id (self->device_list, @@ -3298,11 +4336,22 @@ } /* the system is running with the new firmware version */ - if (g_strcmp0 (fu_device_get_version (dev), - fwupd_release_get_version (rel_history)) == 0) { + if (fu_common_vercmp (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_version (dev_history, fu_device_get_version (dev), + fu_device_get_version_format (dev)); + fu_device_remove_flag (dev_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); 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, @@ -3319,7 +4368,8 @@ return FALSE; /* the plugin either can't tell us the error, or doesn't know itself */ - if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED) { + if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED && + fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { g_debug ("falling back to generic failure"); fu_device_set_update_error (dev_history, "failed to run update on reboot"); } @@ -3355,9 +4405,56 @@ return TRUE; } +static void +fu_engine_udev_uevent_cb (GUdevClient *gudev_client, + const gchar *action, + GUdevDevice *udev_device, + FuEngine *self) +{ + if (g_strcmp0 (action, "add") == 0) { + fu_engine_udev_device_add (self, udev_device); + return; + } + if (g_strcmp0 (action, "remove") == 0) { + fu_engine_udev_device_remove (self, udev_device); + return; + } + if (g_strcmp0 (action, "change") == 0) { + fu_engine_udev_device_changed (self, udev_device); + return; + } +} + +static void +fu_engine_ensure_client_certificate (FuEngine *self) +{ + g_autoptr(FuKeyring) kr = NULL; + g_autoptr(GBytes) blob = g_bytes_new_static ("test\0", 5); + g_autoptr(GBytes) sig = NULL; + g_autoptr(GError) error = NULL; + + /* create keyring and sign dummy data to ensure certificate exists */ + kr = fu_keyring_create_for_kind (FWUPD_KEYRING_KIND_PKCS7, &error); + if (kr == NULL) { + g_message ("failed to create keyring: %s", error->message); + return; + } + if (!fu_keyring_setup (kr, &error)) { + g_message ("failed to setup keyring: %s", error->message); + return; + } + sig = fu_keyring_sign_data (kr, blob, FU_KEYRING_SIGN_FLAG_NONE, &error); + if (sig == NULL) { + g_message ("failed to sign using keyring: %s", error->message); + return; + } + g_debug ("client certificate exists and working"); +} + /** * fu_engine_load: * @self: A #FuEngine + * @flags: #FuEngineLoadFlags, e.g. %FU_ENGINE_LOAD_FLAG_READONLY_FS * @error: A #GError, or %NULL * * Load the firmware update engine so it is ready for use. @@ -3365,31 +4462,56 @@ * Returns: %TRUE for success **/ gboolean -fu_engine_load (FuEngine *self, GError **error) +fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error) { - g_autoptr(AsProfileTask) ptask = NULL; - - /* profile */ - ptask = as_profile_start_literal (self->profile, "FuEngine:load"); - g_assert (ptask != NULL); + FuConfigLoadFlags config_flags = FU_CONFIG_LOAD_FLAG_NONE; + g_autoptr(GPtrArray) checksums = NULL; 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)) { + if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS) + config_flags |= FU_CONFIG_LOAD_FLAG_READONLY_FS; + if (!fu_config_load (self->config, config_flags, error)) { g_prefix_error (error, "Failed to load config: "); return FALSE; } + /* create client certificate */ + fu_engine_ensure_client_certificate (self); + + /* get hardcoded approved firmware */ + checksums = fu_config_get_approved_firmware (self->config); + for (guint i = 0; i < checksums->len; i++) { + const gchar *csum = g_ptr_array_index (checksums, i); + fu_engine_add_approved_firmware (self, csum); + } + + /* get extra firmware saved to the database */ + checksums = fu_history_get_approved_firmware (self->history, error); + if (checksums == NULL) + return FALSE; + for (guint i = 0; i < checksums->len; i++) { + const gchar *csum = g_ptr_array_index (checksums, i); + fu_engine_add_approved_firmware (self, csum); + } + + /* 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)) { + if (!fu_engine_load_metadata_store (self, flags, error)) { g_prefix_error (error, "Failed to load AppStream data: "); return FALSE; } @@ -3424,6 +4546,20 @@ G_CALLBACK (fu_engine_device_changed_cb), self); + /* udev watches can only be set up in _init() so set up client now */ + if (self->udev_subsystems->len > 0) { + g_auto(GStrv) udev_subsystems = g_new0 (gchar *, self->udev_subsystems->len + 1); + for (guint i = 0; i < self->udev_subsystems->len; i++) { + const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i); + udev_subsystems[i] = g_strdup (subsystem); + } + self->gudev_client = g_udev_client_new ((const gchar * const *) udev_subsystems); + g_signal_connect (self->gudev_client, "uevent", + G_CALLBACK (fu_engine_udev_uevent_cb), self); + } + + fu_engine_set_status (self, FWUPD_STATUS_LOADING); + /* add devices */ fu_engine_plugins_setup (self); fu_engine_plugins_coldplug (self, FALSE); @@ -3437,10 +4573,16 @@ self); g_usb_context_enumerate (self->usb_ctx); + /* coldplug udev devices */ + fu_engine_enumerate_udev (self); + /* update the db for devices that were updated during the reboot */ if (!fu_engine_update_history_database (self, error)) return FALSE; + fu_engine_set_status (self, FWUPD_STATUS_IDLE); + self->loaded = TRUE; + /* success */ return TRUE; } @@ -3493,6 +4635,21 @@ g_strdup (version)); } +void +fu_engine_add_app_flag (FuEngine *self, FuAppFlags app_flags) +{ + g_return_if_fail (FU_IS_ENGINE (self)); + self->app_flags |= app_flags; +} + +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) { @@ -3502,35 +4659,34 @@ 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->profile = as_profile_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); + self->approved_firmware = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + 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); -#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, "com.redhat.fwupdate", "12"); + 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 g_hash_table_insert (self->compile_versions, + g_strdup ("com.redhat.fwupdate"), + g_strdup ("12")); + 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", G_USB_MAJOR_VERSION, @@ -3546,21 +4702,25 @@ 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->profile); - 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); g_hash_table_unref (self->compile_versions); + g_hash_table_unref (self->approved_firmware); g_object_unref (self->plugin_list); G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj); diff -Nru fwupd-1.0.9/src/fu-engine.h fwupd-1.2.10/src/fu-engine.h --- fwupd-1.0.9/src/fu-engine.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-engine.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,40 +1,55 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_ENGINE_H -#define __FU_ENGINE_H - -G_BEGIN_DECLS +#pragma once -#include +#include #include #include "fwupd-device.h" #include "fwupd-enums.h" #include "fu-common.h" +#include "fu-keyring.h" #include "fu-install-task.h" #include "fu-plugin.h" +G_BEGIN_DECLS + #define FU_TYPE_ENGINE (fu_engine_get_type ()) G_DECLARE_FINAL_TYPE (FuEngine, fu_engine, FU, ENGINE, GObject) +/** + * FuEngineLoadFlags: + * @FU_ENGINE_LOAD_FLAG_NONE: No flags set + * @FU_ENGINE_LOAD_FLAG_READONLY_FS: Ignore readonly filesystem errors + * + * The flags to use when loading the engine. + **/ +typedef enum { + FU_ENGINE_LOAD_FLAG_NONE = 0, + FU_ENGINE_LOAD_FLAG_READONLY_FS = 1 << 0, + /*< private >*/ + FU_ENGINE_LOAD_FLAG_LAST +} FuEngineLoadFlags; + FuEngine *fu_engine_new (FuAppFlags app_flags); +void fu_engine_add_app_flag (FuEngine *self, + 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, + FuEngineLoadFlags flags, 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); -void fu_engine_profile_dump (FuEngine *self); -gboolean fu_engine_check_plugins_pending (FuEngine *self, - GError **error); -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); @@ -46,6 +61,9 @@ 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, @@ -100,9 +118,7 @@ GError **error); gboolean fu_engine_install_blob (FuEngine *self, FuDevice *device, - GBytes *blob_cab, GBytes *blob_fw, - const gchar *version, FwupdInstallFlags flags, GError **error); gboolean fu_engine_install_tasks (FuEngine *self, @@ -113,6 +129,20 @@ GPtrArray *fu_engine_get_details (FuEngine *self, gint fd, GError **error); +gboolean fu_engine_activate (FuEngine *self, + const gchar *device_id, + GError **error); +GPtrArray *fu_engine_get_approved_firmware (FuEngine *self); +void fu_engine_add_approved_firmware (FuEngine *self, + const gchar *checksum); +gchar *fu_engine_self_sign (FuEngine *self, + const gchar *value, + FuKeyringSignFlags flags, + GError **error); +gboolean fu_engine_modify_config (FuEngine *self, + const gchar *key, + const gchar *value, + GError **error); /* for the self tests */ void fu_engine_add_device (FuEngine *self, @@ -126,8 +156,9 @@ FuInstallTask *task, FwupdInstallFlags flags, 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 - -#endif /* __FU_ENGINE_H */ - diff -Nru fwupd-1.0.9/src/fu-hash.py fwupd-1.2.10/src/fu-hash.py --- fwupd-1.0.9/src/fu-hash.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-hash.py 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,32 @@ +#!/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('#pragma once\n') + f2.write('#define FU_BUILD_HASH "%s"\n' % csum) diff -Nru fwupd-1.0.9/src/fu-history.c fwupd-1.2.10/src/fu-history.c --- fwupd-1.0.9/src/fu-history.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-history.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,16 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuHistory" + #include "config.h" #include #include +#include #include #include #include @@ -16,6 +18,9 @@ #include "fu-common.h" #include "fu-device-private.h" #include "fu-history.h" +#include "fu-mutex.h" + +#define FU_HISTORY_CURRENT_SCHEMA_VERSION 5 static void fu_history_finalize (GObject *object); @@ -23,6 +28,7 @@ { GObject parent_instance; sqlite3 *db; + GRWLock db_mutex; }; G_DEFINE_TYPE (FuHistory, fu_history, G_TYPE_OBJECT) @@ -45,7 +51,6 @@ /* device_id */ tmp = (const gchar *) sqlite3_column_text (stmt, 0); - g_debug ("FuHistory: got sql result %s", tmp); if (tmp != NULL) fwupd_device_set_id (FWUPD_DEVICE (device), tmp); @@ -110,7 +115,17 @@ /* version_old */ tmp = (const gchar *) sqlite3_column_text (stmt, 13); if (tmp != NULL) - fu_device_set_version (device, tmp); + fu_device_set_version (device, tmp, FWUPD_VERSION_FORMAT_UNKNOWN); + + /* 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; } @@ -145,7 +160,7 @@ "CREATE TABLE schema (" "created timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," "version INTEGER DEFAULT 0);" - "INSERT INTO schema (version) VALUES (2);" + "INSERT INTO schema (version) VALUES (0);" "CREATE TABLE history (" "device_id TEXT," "update_state INTEGER DEFAULT 0," @@ -160,11 +175,15 @@ "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);" + "CREATE TABLE approved_firmware (" + "checksum TEXT);" "COMMIT;", NULL, NULL, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Failed to create database: %s", + "Failed to prepare SQL for creating tables: %s", sqlite3_errmsg (self->db)); return FALSE; } @@ -181,7 +200,7 @@ "ALTER TABLE history RENAME TO history_old;", NULL, NULL, NULL); if (rc != SQLITE_OK) { - g_debug ("FuHistory: cannot rename v0 table: %s", sqlite3_errmsg (self->db)); + g_debug ("cannot rename v0 table: %s", sqlite3_errmsg (self->db)); return TRUE; } @@ -191,16 +210,64 @@ /* 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) { - g_debug ("FuHistory: no history to migrate: %s", sqlite3_errmsg (self->db)); + g_debug ("no history to migrate: %s", sqlite3_errmsg (self->db)); return TRUE; } return TRUE; } +static gboolean +fu_history_migrate_database_v2 (FuHistory *self, GError **error) +{ + gint rc; + rc = sqlite3_exec (self->db, + "ALTER TABLE history ADD COLUMN checksum_device 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; + } + return TRUE; +} + +static gboolean +fu_history_migrate_database_v3 (FuHistory *self, GError **error) +{ + gint rc; + 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)); + return TRUE; +} + +static gboolean +fu_history_migrate_database_v4 (FuHistory *self, GError **error) +{ + gint rc; + rc = sqlite3_exec (self->db, + "CREATE TABLE approved_firmware (checksum TEXT);", + NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to create table: %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) @@ -209,14 +276,15 @@ g_autoptr(sqlite3_stmt) stmt = NULL; rc = sqlite3_prepare_v2 (self->db, - "SELECT version FROM schema LIMIT 1;", -1, &stmt, NULL); + "SELECT version FROM schema LIMIT 1;", + -1, &stmt, NULL); if (rc != SQLITE_OK) { g_debug ("no schema version: %s", sqlite3_errmsg (self->db)); return 0; } rc = sqlite3_step (stmt); if (rc != SQLITE_ROW) { - g_warning ("failed to execute prepared statement: %s", + g_warning ("failed prepare to get schema version: %s", sqlite3_errmsg (self->db)); return 0; } @@ -224,6 +292,77 @@ } static gboolean +fu_history_create_or_migrate (FuHistory *self, guint schema_ver, GError **error) +{ + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + + /* create initial up-to-date database or migrate */ + if (schema_ver == 0) { + g_debug ("building initial database"); + if (!fu_history_create_database (self, error)) + return FALSE; + } else if (schema_ver == 1) { + g_debug ("migrating v%u database by recreating table", schema_ver); + if (!fu_history_migrate_database_v1 (self, error)) + return FALSE; + } else if (schema_ver == 2) { + g_debug ("migrating v%u database by altering", schema_ver); + if (!fu_history_migrate_database_v2 (self, error)) + return FALSE; + if (!fu_history_migrate_database_v3 (self, error)) + return FALSE; + if (!fu_history_migrate_database_v4 (self, error)) + return FALSE; + } else if (schema_ver == 3) { + g_debug ("migrating v%u database by altering", schema_ver); + if (!fu_history_migrate_database_v3 (self, error)) + return FALSE; + if (!fu_history_migrate_database_v4 (self, error)) + return FALSE; + } else if (schema_ver == 4) { + g_debug ("migrating v%u database by altering", schema_ver); + if (!fu_history_migrate_database_v4 (self, error)) + return FALSE; + } else { + /* this is probably okay, but return an error if we ever delete + * or rename columns */ + g_warning ("schema version %u is unknown", schema_ver); + return TRUE; + } + + /* set new schema version */ + rc = sqlite3_prepare_v2 (self->db, + "UPDATE schema SET version=?1;", + -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL for updating schema: %s", + sqlite3_errmsg (self->db)); + return FALSE; + } + sqlite3_bind_int (stmt, 1, FU_HISTORY_CURRENT_SCHEMA_VERSION); + return fu_history_stmt_exec (self, stmt, NULL, error); +} + +static gboolean +fu_history_open (FuHistory *self, const gchar *filename, GError **error) +{ + gint rc; + g_debug ("trying to open database '%s'", filename); + rc = sqlite3_open (filename, &self->db); + if (rc != SQLITE_OK) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "Can't open %s: %s", + filename, sqlite3_errmsg (self->db)); + return FALSE; + } + return TRUE; +} + +static gboolean fu_history_load (FuHistory *self, GError **error) { gint rc; @@ -231,6 +370,7 @@ g_autofree gchar *dirname = NULL; g_autofree gchar *filename = NULL; g_autoptr(GFile) file = NULL; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_writer_locker_new (&self->db_mutex); /* already done */ if (self->db != NULL) @@ -238,6 +378,7 @@ g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); g_return_val_if_fail (self->db == NULL, FALSE); + g_return_val_if_fail (locker != NULL, FALSE); /* create directory */ dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); @@ -249,41 +390,44 @@ /* open */ filename = g_build_filename (dirname, "pending.db", NULL); - g_debug ("FuHistory: trying to open database '%s'", filename); - rc = sqlite3_open (filename, &self->db); - if (rc != SQLITE_OK) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "Can't open %s: %s", - filename, sqlite3_errmsg (self->db)); - sqlite3_close (self->db); + if (!fu_history_open (self, filename, error)) return FALSE; - } /* check database */ schema_ver = fu_history_get_schema_version (self); if (schema_ver == 0) { - g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(sqlite3_stmt) stmt_tmp = NULL; rc = sqlite3_prepare_v2 (self->db, "SELECT * FROM history LIMIT 0;", - -1, &stmt, NULL); + -1, &stmt_tmp, NULL); if (rc == SQLITE_OK) schema_ver = 1; } - g_debug ("FuHistory: got schema version of %u", schema_ver); - /* migrate schema */ - if (schema_ver == 0) { - g_debug ("FuHistory: building initial database"); - if (!fu_history_create_database (self, error)) - return FALSE; - } else if (schema_ver == 1) { - g_debug ("FuHistory: migrating v%u database", schema_ver); - if (!fu_history_migrate_database_v1 (self, error)) - return FALSE; + /* create initial up-to-date database, or migrate */ + g_debug ("got schema version of %u", schema_ver); + if (schema_ver != FU_HISTORY_CURRENT_SCHEMA_VERSION) { + g_autoptr(GError) error_migrate = NULL; + if (!fu_history_create_or_migrate (self, schema_ver, &error_migrate)) { + /* this is fatal to the daemon, so delete the database + * and try again with something empty */ + g_warning ("failed to migrate %s database: %s", + filename, error_migrate->message); + sqlite3_close (self->db); + if (g_unlink (filename) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Can't delete %s", filename); + return FALSE; + } + if (!fu_history_open (self, filename, error)) + return FALSE; + return fu_history_create_database (self, error); + } } + /* success */ return TRUE; } @@ -319,6 +463,7 @@ { gint rc; g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); @@ -328,20 +473,23 @@ return FALSE; /* overwrite entry if it exists */ + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); if ((flags & FU_HISTORY_FLAGS_MATCH_OLD_VERSION) && (flags & FU_HISTORY_FLAGS_MATCH_NEW_VERSION)) { - g_debug ("FuHistory: modifying device %s [%s], version not important", + g_debug ("modifying device %s [%s], version not important", fu_device_get_name (device), fu_device_get_id (device)); rc = sqlite3_prepare_v2 (self->db, "UPDATE history SET " "update_state = ?1, " "update_error = ?2, " + "checksum_device = ?6, " "flags = ?3 " "WHERE device_id = ?4;", -1, &stmt, NULL); } else if (flags & FU_HISTORY_FLAGS_MATCH_OLD_VERSION) { - g_debug ("FuHistory: modifying device %s [%s], only version old %s", + g_debug ("modifying device %s [%s], only version old %s", fu_device_get_name (device), fu_device_get_id (device), fu_device_get_version (device)); @@ -349,11 +497,12 @@ "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); } else if (flags & FU_HISTORY_FLAGS_MATCH_NEW_VERSION) { - g_debug ("FuHistory: modifying device %s [%s], only version new %s", + g_debug ("modifying device %s [%s], only version new %s", fu_device_get_name (device), fu_device_get_id (device), fu_device_get_version (device)); @@ -361,6 +510,7 @@ "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); @@ -369,25 +519,30 @@ } if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Failed to prepare SQL: %s", + "Failed to prepare SQL to update history: %s", 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; g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); @@ -401,18 +556,22 @@ if (!fu_history_remove_device (self, device, release, error)) return FALSE; - g_debug ("FuHistory: add device %s [%s]", + g_debug ("add device %s [%s]", fu_device_get_name (device), fu_device_get_id (device)); if (release != NULL) { 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)); /* add */ + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); rc = sqlite3_prepare_v2 (self->db, "INSERT INTO history (device_id," "update_state," @@ -427,12 +586,14 @@ "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", + "Failed to prepare SQL to insert history: %s", sqlite3_errmsg (self->db)); return FALSE; } @@ -450,6 +611,8 @@ 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); } @@ -460,6 +623,7 @@ { gint rc; g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); @@ -468,14 +632,16 @@ return FALSE; /* remove entries */ - g_debug ("FuHistory: removing all devices with update_state %s", + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); + g_debug ("removing all devices with update_state %s", fwupd_update_state_to_string (update_state)); rc = sqlite3_prepare_v2 (self->db, "DELETE FROM history WHERE update_state = ?1", -1, &stmt, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Failed to prepare SQL: %s", + "Failed to prepare SQL to delete history: %s", sqlite3_errmsg (self->db)); return FALSE; } @@ -488,6 +654,7 @@ { gint rc; g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); @@ -496,11 +663,13 @@ return FALSE; /* remove entries */ - g_debug ("FuHistory: removing all devices"); + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); + g_debug ("removing all devices"); rc = sqlite3_prepare_v2 (self->db, "DELETE FROM history;", -1, &stmt, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Failed to prepare SQL: %s", + "Failed to prepare SQL to delete history: %s", sqlite3_errmsg (self->db)); return FALSE; } @@ -513,6 +682,7 @@ { gint rc; g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); @@ -522,7 +692,9 @@ if (!fu_history_load (self, error)) return FALSE; - g_debug ("FuHistory: remove device %s [%s]", + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); + g_debug ("remove device %s [%s]", fu_device_get_name (device), fu_device_get_id (device)); rc = sqlite3_prepare_v2 (self->db, @@ -532,7 +704,7 @@ -1, &stmt, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Failed to prepare SQL: %s", + "Failed to prepare SQL to delete history: %s", sqlite3_errmsg (self->db)); return FALSE; } @@ -548,6 +720,7 @@ gint rc; g_autoptr(GPtrArray) array_tmp = NULL; g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; g_return_val_if_fail (FU_IS_HISTORY (self), NULL); g_return_val_if_fail (device_id != NULL, NULL); @@ -557,7 +730,9 @@ return NULL; /* get all the devices */ - g_debug ("FuHistory: get device"); + locker = g_rw_lock_reader_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, NULL); + g_debug ("get device"); rc = sqlite3_prepare_v2 (self->db, "SELECT device_id, " "checksum, " @@ -572,11 +747,14 @@ "update_state, " "update_error, " "version_new, " - "version_old FROM history WHERE " - "device_id = ?1 LIMIT 1", -1, &stmt, NULL); + "version_old, " + "checksum_device, " + "protocol FROM history WHERE " + "device_id = ?1 ORDER BY device_created DESC " + "LIMIT 1", -1, &stmt, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Failed to prepare SQL: %s", + "Failed to prepare SQL to get history: %s", sqlite3_errmsg (self->db)); return NULL; } @@ -601,6 +779,7 @@ g_autoptr(sqlite3_stmt) stmt = NULL; gint rc; g_autoptr(GPtrArray) array_tmp = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; g_return_val_if_fail (FU_IS_HISTORY (self), NULL); @@ -611,7 +790,8 @@ } /* get all the devices */ - g_debug ("FuHistory: get devices"); + locker = g_rw_lock_reader_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, NULL); rc = sqlite3_prepare_v2 (self->db, "SELECT device_id, " "checksum, " @@ -626,12 +806,14 @@ "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) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Failed to prepare SQL: %s", + "Failed to prepare SQL to get history: %s", sqlite3_errmsg (self->db)); return NULL; } @@ -642,6 +824,108 @@ return array; } +GPtrArray * +fu_history_get_approved_firmware (FuHistory *self, GError **error) +{ + gint rc; + g_autoptr(GRWLockReaderLocker) locker = NULL; + g_autoptr(GPtrArray) array = NULL; + g_autoptr(sqlite3_stmt) stmt = NULL; + + g_return_val_if_fail (FU_IS_HISTORY (self), NULL); + + /* lazy load */ + if (self->db == NULL) { + if (!fu_history_load (self, error)) + return NULL; + } + + /* get all the approved firmware */ + locker = g_rw_lock_reader_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, NULL); + rc = sqlite3_prepare_v2 (self->db, + "SELECT checksum FROM approved_firmware;", + -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL to get checksum: %s", + sqlite3_errmsg (self->db)); + return NULL; + } + array = g_ptr_array_new_with_free_func (g_free); + while ((rc = sqlite3_step (stmt)) == SQLITE_ROW) { + const gchar *tmp = (const gchar *) sqlite3_column_text (stmt, 0); + g_ptr_array_add (array, g_strdup (tmp)); + } + if (rc != SQLITE_DONE) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_WRITE, + "failed to execute prepared statement: %s", + sqlite3_errmsg (self->db)); + return NULL; + } + return g_steal_pointer (&array); +} + +gboolean +fu_history_clear_approved_firmware (FuHistory *self, GError **error) +{ + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; + + g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); + + /* lazy load */ + if (!fu_history_load (self, error)) + return FALSE; + + /* remove entries */ + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); + rc = sqlite3_prepare_v2 (self->db, + "DELETE FROM approved_firmware;", + -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL to delete approved firmware: %s", + sqlite3_errmsg (self->db)); + return FALSE; + } + return fu_history_stmt_exec (self, stmt, NULL, error); +} + +gboolean +fu_history_add_approved_firmware (FuHistory *self, + const gchar *checksum, + GError **error) +{ + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockReaderLocker) locker = NULL; + + g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); + g_return_val_if_fail (checksum != NULL, FALSE); + + /* lazy load */ + if (!fu_history_load (self, error)) + return FALSE; + + /* add */ + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); + rc = sqlite3_prepare_v2 (self->db, + "INSERT INTO approved_firmware (checksum) " + "VALUES (?1)", -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL to insert checksum: %s", + sqlite3_errmsg (self->db)); + return FALSE; + } + sqlite3_bind_text (stmt, 1, checksum, -1, SQLITE_STATIC); + return fu_history_stmt_exec (self, stmt, NULL, error); +} + static void fu_history_class_init (FuHistoryClass *klass) { @@ -652,6 +936,7 @@ static void fu_history_init (FuHistory *self) { + g_rw_lock_init (&self->db_mutex); } static void @@ -661,6 +946,7 @@ if (self->db != NULL) sqlite3_close (self->db); + g_rw_lock_clear (&self->db_mutex); G_OBJECT_CLASS (fu_history_parent_class)->finalize (object); } diff -Nru fwupd-1.0.9/src/fu-history.h fwupd-1.2.10/src/fu-history.h --- fwupd-1.0.9/src/fu-history.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-history.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_HISTORY_H -#define __FU_HISTORY_H +#pragma once #include @@ -58,7 +56,12 @@ GPtrArray *fu_history_get_devices (FuHistory *self, GError **error); -G_END_DECLS - -#endif /* __FU_HISTORY_H */ +gboolean fu_history_clear_approved_firmware (FuHistory *self, + GError **error); +gboolean fu_history_add_approved_firmware (FuHistory *self, + const gchar *checksum, + GError **error); +GPtrArray *fu_history_get_approved_firmware (FuHistory *self, + GError **error); +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-hwids.c fwupd-1.2.10/src/fu-hwids.c --- fwupd-1.0.9/src/fu-hwids.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-hwids.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,18 +1,20 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuHwids" + #include "config.h" #include #include #include -#include +#include "fu-common.h" #include "fu-hwids.h" +#include "fwupd-common.h" #include "fwupd-error.h" struct _FuHwids { @@ -20,6 +22,7 @@ GHashTable *hash_dmi_hw; /* BiosVersion->"1.2.3 " */ GHashTable *hash_dmi_display; /* BiosVersion->"1.2.3" */ GHashTable *hash_guid; /* a-c-b-d->1 */ + GPtrArray *array_guids; /* a-c-b-d */ }; G_DEFINE_TYPE (FuHwids, fu_hwids, G_TYPE_OBJECT) @@ -55,10 +58,23 @@ return g_hash_table_lookup (self->hash_guid, guid) != NULL; } +/** + * fu_hwids_get_guids: + * @self: A #FuHwids + * + * Returns all the defined HWIDs + * + * Returns: (transfer none) (element-type utf-8): An array of GUIDs + **/ +GPtrArray * +fu_hwids_get_guids (FuHwids *self) +{ + return self->array_guids; +} + static gchar * fu_hwids_get_guid_for_str (const gchar *str, GError **error) { - const gchar *namespace_id = "70ffd812-4c7f-4c7d-0000-000000000000"; glong items_written = 0; g_autofree gunichar2 *data = NULL; @@ -80,10 +96,8 @@ 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 fwupd_guid_hash_data ((guint8*) data, items_written * 2, + FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT); } /** @@ -245,7 +259,8 @@ tmp = fu_smbios_get_string (smbios, type, offset, error); if (tmp == NULL) return NULL; - return g_strdup (tmp); + /* ComputerHardwareIds.exe seems to strip spaces */ + return fu_common_strstrip (tmp); } static gchar * @@ -376,7 +391,6 @@ 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 */ @@ -389,10 +403,7 @@ g_hash_table_insert (self->hash_guid, g_strdup (guid), GUINT_TO_POINTER (1)); - - /* 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; @@ -408,6 +419,8 @@ g_hash_table_unref (self->hash_dmi_hw); g_hash_table_unref (self->hash_dmi_display); g_hash_table_unref (self->hash_guid); + g_ptr_array_unref (self->array_guids); + G_OBJECT_CLASS (fu_hwids_parent_class)->finalize (object); } @@ -424,6 +437,7 @@ self->hash_dmi_hw = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); self->hash_dmi_display = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); self->hash_guid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + self->array_guids = g_ptr_array_new_with_free_func (g_free); } FuHwids * diff -Nru fwupd-1.0.9/src/fu-hwids.h fwupd-1.2.10/src/fu-hwids.h --- fwupd-1.0.9/src/fu-hwids.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-hwids.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_HWIDS_H -#define __FU_HWIDS_H +#pragma once #include @@ -42,6 +40,7 @@ gchar *fu_hwids_get_guid (FuHwids *self, const gchar *keys, GError **error); +GPtrArray *fu_hwids_get_guids (FuHwids *self); gboolean fu_hwids_has_guid (FuHwids *self, const gchar *guid); gboolean fu_hwids_setup (FuHwids *self, @@ -49,5 +48,3 @@ GError **error); G_END_DECLS - -#endif /* __FU_HWIDS_H */ diff -Nru fwupd-1.0.9/src/fu-idle.c fwupd-1.2.10/src/fu-idle.c --- fwupd-1.0.9/src/fu-idle.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-idle.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 */ + GRWLock 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(GRWLockReaderLocker) locker = g_rw_lock_writer_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(GRWLockReaderLocker) locker = g_rw_lock_writer_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); + g_rw_lock_init (&self->items_mutex); +} + +static void +fu_idle_finalize (GObject *obj) +{ + FuIdle *self = FU_IDLE (obj); + + fu_idle_stop (self); + g_ptr_array_unref (self->items); + g_rw_lock_clear (&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 -Nru fwupd-1.0.9/src/fu-idle.h fwupd-1.2.10/src/fu-idle.h --- fwupd-1.0.9/src/fu-idle.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-idle.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#include "fu-device.h" + +G_BEGIN_DECLS + +#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); + +/** + * FuIdleLocker: + * @idle: A #FuIdle + * @token: A #guint32 number + * + * A locker to prevent daemon from shutting down on its own + **/ +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 diff -Nru fwupd-1.0.9/src/fu-install-task.c fwupd-1.2.10/src/fu-install-task.c --- fwupd-1.0.9/src/fu-install-task.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-install-task.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,16 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuInstallTask" + #include "config.h" #include +#include "fu-common-version.h" #include "fu-device-private.h" #include "fu-install-task.h" #include "fu-keyring-utils.h" @@ -17,8 +19,8 @@ { GObject parent_instance; FuDevice *device; - AsApp *app; - FwupdTrustFlags trust_flags; + XbNode *component; + FwupdReleaseFlags trust_flags; gboolean is_downgrade; }; @@ -40,18 +42,18 @@ } /** - * 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; } /** @@ -63,9 +65,9 @@ * NOTE: This is only set after fu_install_task_check_requirements() has been * called successfully. * - * Returns: the #FwupdTrustFlags, e.g. #FWUPD_TRUST_FLAG_PAYLOAD + * Returns: the #FwupdReleaseFlags, e.g. #FWUPD_TRUST_FLAG_PAYLOAD **/ -FwupdTrustFlags +FwupdReleaseFlags fu_install_task_get_trust_flags (FuInstallTask *self) { g_return_val_if_fail (FU_IS_INSTALL_TASK (self), FALSE); @@ -107,24 +109,34 @@ FwupdInstallFlags flags, GError **error) { - AsRelease *release; - GPtrArray *provides; + const gchar *tmp; 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; } @@ -161,6 +173,7 @@ /* called with online update, test if device is supposed to allow this */ if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) == 0 && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && fu_device_has_flag (self->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) { g_set_error (error, FWUPD_ERROR, @@ -184,7 +197,7 @@ } /* 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, @@ -196,7 +209,7 @@ } /* 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, @@ -205,19 +218,62 @@ return FALSE; } + /* check the version formats match if set in the release */ + tmp = xb_node_query_text (self->component, "custom/value[@key='LVFS::VersionFormat']", NULL); + if (tmp != NULL) { + FwupdVersionFormat fmt_dev = fu_device_get_version_format (self->device); + FwupdVersionFormat fmt_rel = fwupd_version_format_from_string (tmp); + if (fmt_rel == FWUPD_VERSION_FORMAT_UNKNOWN && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "release version format '%s' unsupported", + tmp); + return FALSE; + } + if (fmt_dev == FWUPD_VERSION_FORMAT_UNKNOWN && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "release version format '%s' but no device version format", + tmp); + return FALSE; + } + if (fmt_dev != fmt_rel) { + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Firmware version formats were different, " + "device was '%s' and release is '%s'", + fwupd_version_format_to_string (fmt_dev), + fwupd_version_format_to_string (fmt_rel)); + return FALSE; + } + g_warning ("ignoring version format difference %s:%s", + fwupd_version_format_to_string (fmt_dev), + fwupd_version_format_to_string (fmt_rel)); + } + } + + /* 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) { + if (version_lowest != NULL && + fu_common_vercmp (version_lowest, version) > 0 && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_VERSION_NEWER, "Specified firmware is older than the minimum " - "required version '%s < %s'", version_lowest, version); + "required version '%s < %s'", version, version_lowest); return FALSE; } /* 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, @@ -237,7 +293,17 @@ } /* verify */ - return fu_keyring_get_release_trust_flags (release, &self->trust_flags, error); + if (!fu_keyring_get_release_flags (release, &self->trust_flags, &error_local)) { + if (g_error_matches (error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning ("Ignoring verification for %s: %s", + fu_device_get_name (self->device), + error_local->message); + } else { + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + } + return TRUE; } /** @@ -279,7 +345,7 @@ { 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); @@ -317,18 +383,18 @@ /** * 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 -Nru fwupd-1.0.9/src/fu-install-task.h fwupd-1.2.10/src/fu-install-task.h --- fwupd-1.0.9/src/fu-install-task.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-install-task.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,13 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_INSTALL_TASK_H -#define __FU_INSTALL_TASK_H +#pragma once #include -#include +#include #include "fu-device.h" @@ -19,10 +17,10 @@ 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); -FwupdTrustFlags fu_install_task_get_trust_flags (FuInstallTask *self); +XbNode *fu_install_task_get_component (FuInstallTask *self); +FwupdReleaseFlags 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, FwupdInstallFlags flags, @@ -32,6 +30,3 @@ FuInstallTask *task2); G_END_DECLS - -#endif /* __FU_INSTALL_TASK_H */ - diff -Nru fwupd-1.0.9/src/fu-io-channel.c fwupd-1.2.10/src/fu-io-channel.c --- fwupd-1.0.9/src/fu-io-channel.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-io-channel.c 2019-07-15 18:25:54.000000000 +0000 @@ -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 NULL; + } + 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 -Nru fwupd-1.0.9/src/fu-io-channel.h fwupd-1.2.10/src/fu-io-channel.h --- fwupd-1.0.9/src/fu-io-channel.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-io-channel.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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 diff -Nru fwupd-1.0.9/src/fu-keyring.c fwupd-1.2.10/src/fu-keyring.c --- fwupd-1.0.9/src/fu-keyring.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuKeyring" + #include "config.h" #include "fwupd-error.h" @@ -39,13 +40,33 @@ fu_keyring_verify_data (FuKeyring *keyring, GBytes *blob, GBytes *blob_signature, + FuKeyringVerifyFlags flags, GError **error) { FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring); g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL); g_return_val_if_fail (blob != NULL, NULL); g_return_val_if_fail (blob_signature != NULL, NULL); - return klass->verify_data (keyring, blob, blob_signature, error); + return klass->verify_data (keyring, blob, blob_signature, flags, error); +} + +GBytes * +fu_keyring_sign_data (FuKeyring *keyring, + GBytes *blob, + FuKeyringSignFlags flags, + GError **error) +{ + FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring); + g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL); + g_return_val_if_fail (blob != NULL, NULL); + if (klass->sign_data == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "signing data is not supported"); + return NULL; + } + return klass->sign_data (keyring, blob, flags, error); } const gchar * diff -Nru fwupd-1.0.9/src/fu-keyring-gpg.c fwupd-1.2.10/src/fu-keyring-gpg.c --- fwupd-1.0.9/src/fu-keyring-gpg.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-gpg.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuKeyring" + #include "config.h" #include @@ -230,6 +231,7 @@ fu_keyring_gpg_verify_data (FuKeyring *keyring, GBytes *blob, GBytes *blob_signature, + FuKeyringVerifyFlags flags, GError **error) { FuKeyringGpg *self = FU_KEYRING_GPG (keyring); @@ -241,6 +243,15 @@ g_auto(gpgme_data_t) sig = NULL; g_autoptr(GString) authority_newest = g_string_new (NULL); + /* not supported */ + if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no GPG client certificate support"); + return NULL; + } + /* load file data */ rc = gpgme_data_new_from_mem (&data, g_bytes_get_data (blob, NULL), diff -Nru fwupd-1.0.9/src/fu-keyring-gpg.h fwupd-1.2.10/src/fu-keyring-gpg.h --- fwupd-1.0.9/src/fu-keyring-gpg.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-gpg.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_KEYRING_GPG_H -#define __FU_KEYRING_GPG_H +#pragma once #include "fu-keyring.h" @@ -19,5 +17,3 @@ FuKeyring *fu_keyring_gpg_new (void); G_END_DECLS - -#endif /* __FU_KEYRING_GPG_H */ diff -Nru fwupd-1.0.9/src/fu-keyring.h fwupd-1.2.10/src/fu-keyring.h --- fwupd-1.0.9/src/fu-keyring.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_KEYRING_H -#define __FU_KEYRING_H +#pragma once #include #include @@ -18,6 +16,36 @@ #define FU_TYPE_KEYRING (fu_keyring_get_type ()) G_DECLARE_DERIVABLE_TYPE (FuKeyring, fu_keyring, FU, KEYRING, GObject) +/** + * FuKeyringVerifyFlags: + * @FU_KEYRING_VERIFY_FLAG_NONE: No flags set + * @FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT: Use client certificate to verify + * + * The flags to use when interacting with a keyring + **/ +typedef enum { + FU_KEYRING_VERIFY_FLAG_NONE = 0, + FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT = 1 << 1, + /*< private >*/ + FU_KEYRING_VERIFY_FLAG_LAST +} FuKeyringVerifyFlags; + +/** + * FuKeyringSignFlags: + * @FU_KEYRING_SIGN_FLAG_NONE: No flags set + * @FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP: Add a timestamp + * @FU_KEYRING_SIGN_FLAG_ADD_CERT: Add a certificate + * + * The flags to when signing a binary + **/ +typedef enum { + FU_KEYRING_SIGN_FLAG_NONE = 0, + FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP = 1 << 0, + FU_KEYRING_SIGN_FLAG_ADD_CERT = 1 << 1, + /*< private >*/ + FU_KEYRING_SIGN_FLAG_LAST +} FuKeyringSignFlags; + struct _FuKeyringClass { GObjectClass parent_class; @@ -29,6 +57,11 @@ FuKeyringResult *(*verify_data) (FuKeyring *keyring, GBytes *payload, GBytes *payload_signature, + FuKeyringVerifyFlags flags, + GError **error); + GBytes *(*sign_data) (FuKeyring *keyring, + GBytes *payload, + FuKeyringSignFlags flags, GError **error); }; @@ -40,11 +73,14 @@ FuKeyringResult *fu_keyring_verify_data (FuKeyring *keyring, GBytes *blob, GBytes *blob_signature, + FuKeyringVerifyFlags flags, + GError **error); +GBytes *fu_keyring_sign_data (FuKeyring *keyring, + GBytes *blob, + FuKeyringSignFlags flags, GError **error); const gchar *fu_keyring_get_name (FuKeyring *self); void fu_keyring_set_name (FuKeyring *self, const gchar *name); G_END_DECLS - -#endif /* __FU_KEYRING_H */ diff -Nru fwupd-1.0.9/src/fu-keyring-pkcs7.c fwupd-1.2.10/src/fu-keyring-pkcs7.c --- fwupd-1.0.9/src/fu-keyring-pkcs7.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-pkcs7.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,18 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuKeyring" + #include "config.h" +#include +#include #include +#include "fu-common.h" #include "fu-keyring-pkcs7.h" #include "fwupd-error.h" @@ -21,60 +25,95 @@ G_DEFINE_TYPE (FuKeyringPkcs7, fu_keyring_pkcs7, FU_TYPE_KEYRING) +typedef guchar gnutls_data_t; + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pkcs7_t, gnutls_pkcs7_deinit, NULL) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_privkey_t, gnutls_privkey_deinit, NULL) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pubkey_t, gnutls_pubkey_deinit, NULL) G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_crt_t, gnutls_x509_crt_deinit, NULL) G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_dn_t, gnutls_x509_dn_deinit, NULL) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_privkey_t, gnutls_x509_privkey_deinit, NULL) +#ifdef HAVE_GNUTLS_3_6_0 +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_spki_t, gnutls_x509_spki_deinit, NULL) +#endif +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_data_t, gnutls_free) #pragma clang diagnostic pop -static gboolean -fu_keyring_pkcs7_add_public_key (FuKeyringPkcs7 *self, - const gchar *filename, - gnutls_x509_crt_fmt_t format, - GError **error) +static gnutls_x509_crt_t +fu_keyring_pkcs7_load_crt_from_filename (const gchar *filename, + gnutls_x509_crt_fmt_t format, + GError **error) { - gnutls_datum_t datum; - gsize sz; + gnutls_datum_t d = { 0 }; + gsize bufsz = 0; int rc; - g_autofree gchar *pem_data = NULL; - g_auto(gnutls_x509_crt_t) cert = NULL; + g_autofree gchar *buf = NULL; + g_auto(gnutls_x509_crt_t) crt = NULL; - /* load file and add to the trust list */ - if (!g_file_get_contents (filename, &pem_data, &sz, error)) { - g_prefix_error (error, "failed to load %s: ", filename); - return FALSE; + /* create certificate */ + rc = gnutls_x509_crt_init (&crt); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "crt_init: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; } - datum.data = (guint8 *) pem_data; - datum.size = sz; - g_debug ("trying to load CA from %s", filename); - rc = gnutls_x509_crt_init (&cert); + + /* import the certificate */ + if (!g_file_get_contents (filename, &buf, &bufsz, error)) + return NULL; + d.size = bufsz; + d.data = (unsigned char *) buf; + rc = gnutls_x509_crt_import (crt, &d, GNUTLS_X509_FMT_PEM); if (rc < 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID, - "failed to initialize certificate: %s [%i]", + "crt_import: %s [%i]", gnutls_strerror (rc), rc); - return FALSE; + return NULL; } - rc = gnutls_x509_crt_import (cert, &datum, format); + return g_steal_pointer (&crt); +} + +static gboolean +fu_keyring_pkcs7_add_public_key (FuKeyringPkcs7 *self, + const gchar *filename, + gnutls_x509_crt_fmt_t format, + GError **error) +{ + guint key_usage = 0; + int rc; + g_auto(gnutls_x509_crt_t) crt = NULL; + + /* load file and add to the trust list */ + g_debug ("trying to load certificate from %s", filename); + crt = fu_keyring_pkcs7_load_crt_from_filename (filename, format, error); + if (crt == NULL) + return FALSE; + rc = gnutls_x509_crt_get_key_usage (crt, &key_usage, NULL); if (rc < 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID, - "failed to import certificate: %s [%i]", + "failed to get key usage: %s [%i]", gnutls_strerror (rc), rc); return FALSE; } - if (gnutls_x509_crt_check_key_purpose (cert, GNUTLS_KP_ANY, 0) != 0) { + if ((key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) == 0 && + (key_usage & GNUTLS_KEY_KEY_CERT_SIGN) == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID, - "certificate %s not suitable for use", - filename); + "certificate %s not suitable for use [0x%x]", + filename, key_usage); return FALSE; } - rc = gnutls_x509_trust_list_add_cas (self->tl, &cert, 1, 0); + rc = gnutls_x509_trust_list_add_cas (self->tl, &crt, 1, 0); if (rc < 0) { g_set_error (error, FWUPD_ERROR, @@ -83,10 +122,10 @@ gnutls_strerror (rc), rc); return FALSE; } - g_debug ("loaded %i CAs", rc); + g_debug ("loaded %i certificates", rc); /* confusingly the trust list does not copy the certificate */ - cert = NULL; + crt = NULL; return TRUE; } @@ -124,6 +163,362 @@ return TRUE; } +static gnutls_privkey_t +fu_keyring_pkcs7_load_privkey (FuKeyringPkcs7 *self, GError **error) +{ + int rc; + gnutls_datum_t d = { 0 }; + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + g_auto(gnutls_privkey_t) key = NULL; + + /* load the private key */ + rc = gnutls_privkey_init (&key); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "privkey_init: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; + } + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_build_filename (localstatedir, "pki", "secret.key", NULL); + if (!g_file_get_contents (fn, &buf, &bufsz, error)) + return NULL; + d.size = bufsz; + d.data = (unsigned char *) buf; + rc = gnutls_privkey_import_x509_raw (key, &d, GNUTLS_X509_FMT_PEM, NULL, 0); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "privkey_import_x509_raw: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; + } + return g_steal_pointer (&key); +} + +static gnutls_x509_crt_t +fu_keyring_pkcs7_load_client_certificate (FuKeyringPkcs7 *self, GError **error) +{ + g_autofree gchar *filename = NULL; + g_autofree gchar *localstatedir = NULL; + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + filename = g_build_filename (localstatedir, "pki", "client.pem", NULL); + return fu_keyring_pkcs7_load_crt_from_filename (filename, + GNUTLS_X509_FMT_PEM, + error); +} + +static gnutls_pubkey_t +fu_keyring_pkcs7_load_pubkey_from_privkey (gnutls_privkey_t privkey, GError **error) +{ + g_auto(gnutls_pubkey_t) pubkey = NULL; + int rc; + + /* get the public key part of the private key */ + rc = gnutls_pubkey_init (&pubkey); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "pubkey_init: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; + } + rc = gnutls_pubkey_import_privkey (pubkey, privkey, 0, 0); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "pubkey_import_privkey: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; + } + + /* success */ + return g_steal_pointer (&pubkey); +} + +/* generates a private key just like: + * `certtool --generate-privkey` */ +static gboolean +fu_keyring_pkcs7_ensure_private_key (FuKeyringPkcs7 *self, GError **error) +{ +#ifdef HAVE_GNUTLS_3_6_0 + gnutls_datum_t d = { 0 }; + int bits; + int key_type = GNUTLS_PK_RSA; + int rc; + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + g_auto(gnutls_x509_privkey_t) key = NULL; + g_auto(gnutls_x509_spki_t) spki = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(gnutls_data_t) d_payload = NULL; + + /* check exists */ + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_build_filename (localstatedir, "pki", "secret.key", NULL); + if (g_file_test (fn, G_FILE_TEST_EXISTS)) + return TRUE; + + /* initialize key and SPKI */ + rc = gnutls_x509_privkey_init (&key); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "privkey_init: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + rc = gnutls_x509_spki_init (&spki); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "spki_init: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* generate key */ + bits = gnutls_sec_param_to_pk_bits (key_type, GNUTLS_SEC_PARAM_HIGH); + g_debug ("generating a %d bit %s private key...", + bits, gnutls_pk_algorithm_get_name (key_type)); + rc = gnutls_x509_privkey_generate2(key, key_type, bits, 0, NULL, 0); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "privkey_generate2: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + rc = gnutls_x509_privkey_verify_params (key); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "privkey_verify_params: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* create parents if required */ + if (!fu_common_mkdir_parent (fn, error)) + return FALSE; + + /* save to file */ + rc = gnutls_x509_privkey_export2 (key, GNUTLS_X509_FMT_PEM, &d); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "privkey_export2: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + d_payload = d.data; + file = g_file_new_for_path (fn); + return g_file_replace_contents (file, (const char *) d_payload, d.size, + NULL, FALSE, G_FILE_CREATE_PRIVATE, NULL, + NULL, error); +#else + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot build private key as GnuTLS version is too old"); + return FALSE; +#endif +} + +/* generates a self signed certificate just like: + * `certtool --generate-self-signed --load-privkey priv.pem` */ +static gboolean +fu_keyring_pkcs7_ensure_client_certificate (FuKeyringPkcs7 *self, GError **error) +{ + int rc; + gnutls_datum_t d = { 0 }; + guchar sha1buf[20]; + gsize sha1bufsz = sizeof(sha1buf); + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + g_auto(gnutls_privkey_t) key = NULL; + g_auto(gnutls_pubkey_t) pubkey = NULL; + g_auto(gnutls_x509_crt_t) crt = NULL; + g_autoptr(gnutls_data_t) d_payload = NULL; + + /* check exists */ + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_build_filename (localstatedir, "pki", "client.pem", NULL); + if (g_file_test (fn, G_FILE_TEST_EXISTS)) + return TRUE; + + /* ensure the private key exists */ + if (!fu_keyring_pkcs7_ensure_private_key (self, error)) { + g_prefix_error (error, "failed to generate private key: "); + return FALSE; + } + + /* load private key */ + key = fu_keyring_pkcs7_load_privkey (self, error); + if (key == NULL) + return FALSE; + + /* load the public key from the private key */ + pubkey = fu_keyring_pkcs7_load_pubkey_from_privkey (key, error); + if (pubkey == NULL) + return FALSE; + + /* create certificate */ + rc = gnutls_x509_crt_init (&crt); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "crt_init: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set public key */ + rc = gnutls_x509_crt_set_pubkey (crt, pubkey); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "crt_set_pubkey: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set positive random serial number */ + rc = gnutls_rnd (GNUTLS_RND_NONCE, sha1buf, sizeof(sha1buf)); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "gnutls_rnd: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + sha1buf[0] &= 0x7f; + rc = gnutls_x509_crt_set_serial(crt, sha1buf, sizeof(sha1buf)); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "crt_set_serial: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set activation */ + rc = gnutls_x509_crt_set_activation_time (crt, time (NULL)); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "set_activation_time: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set expiration */ + rc = gnutls_x509_crt_set_expiration_time (crt, (time_t) -1); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "set_expiration_time: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set basic constraints */ + rc = gnutls_x509_crt_set_basic_constraints (crt, 0, -1); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "set_basic_constraints: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set usage */ + rc = gnutls_x509_crt_set_key_usage (crt, GNUTLS_KEY_DIGITAL_SIGNATURE); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "set_key_usage: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set subject key ID */ + rc = gnutls_x509_crt_get_key_id (crt, GNUTLS_KEYID_USE_SHA1, sha1buf, &sha1bufsz); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "get_key_id: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + rc = gnutls_x509_crt_set_subject_key_id (crt, sha1buf, sha1bufsz); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "set_subject_key_id: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* set version */ + rc = gnutls_x509_crt_set_version (crt, 3); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "error setting certificate version: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* self-sign certificate */ + rc = gnutls_x509_crt_privkey_sign (crt, crt, key, GNUTLS_DIG_SHA256, 0); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "crt_privkey_sign: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + + /* export to file */ + rc = gnutls_x509_crt_export2 (crt, GNUTLS_X509_FMT_PEM, &d); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "crt_export2: %s [%i]", + gnutls_strerror (rc), rc); + return FALSE; + } + d_payload = d.data; + return g_file_set_contents (fn, (const gchar *) d_payload, d.size, error); +} + static gboolean fu_keyring_pkcs7_setup (FuKeyring *keyring, GError **error) { @@ -171,24 +566,29 @@ if (rc < 0) return NULL; str = (gnutls_datum_t *) gnutls_malloc (sizeof (gnutls_datum_t)); + str->data = NULL; rc = gnutls_x509_dn_get_str2 (dn, str, 0); if (rc < 0) return NULL; return g_strndup ((const gchar *) str->data, str->size); } +/* verifies a detached signature just like: + * `certtool --p7-verify --load-certificate client.pem --infile=test.p7b` */ static FuKeyringResult * fu_keyring_pkcs7_verify_data (FuKeyring *keyring, GBytes *blob, GBytes *blob_signature, + FuKeyringVerifyFlags flags, GError **error) { FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring); - gnutls_datum_t datum; + gnutls_datum_t datum = { 0 }; gint64 timestamp_newest = 0; int count; int rc; g_auto(gnutls_pkcs7_t) pkcs7 = NULL; + g_auto(gnutls_x509_crt_t) crt = NULL; g_autoptr(GString) authority_newest = g_string_new (NULL); /* startup */ @@ -227,17 +627,33 @@ "no PKCS7 signatures found"); return NULL; } + + /* use client certificate */ + if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) { + if (!fu_keyring_pkcs7_ensure_client_certificate (self, error)) { + g_prefix_error (error, "failed to generate client certificate: "); + return NULL; + } + crt = fu_keyring_pkcs7_load_client_certificate (self, error); + if (crt == NULL) + return NULL; + } + for (gint i = 0; i < count; i++) { gnutls_pkcs7_signature_info_st info; gint64 signing_time = 0; /* verify the data against the detached signature */ - rc = gnutls_pkcs7_verify (pkcs7, self->tl, - NULL, /* vdata */ - 0, /* vdata_size */ - i, /* index */ - &datum, /* data */ - 0); /* flags */ + if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) { + rc = gnutls_pkcs7_verify_direct (pkcs7, crt, i, &datum, 0); + } else { + rc = gnutls_pkcs7_verify (pkcs7, self->tl, + NULL, /* vdata */ + 0, /* vdata_size */ + i, /* index */ + &datum, /* data */ + 0); /* flags */ + } if (rc < 0) { g_set_error (error, FWUPD_ERROR, @@ -262,7 +678,8 @@ g_autofree gchar *dn = NULL; timestamp_newest = signing_time; dn = fu_keyring_pkcs7_datum_to_dn_str (&info.issuer_dn); - g_string_assign (authority_newest, dn); + if (dn != NULL) + g_string_assign (authority_newest, dn); } gnutls_pkcs7_signature_info_deinit (&info); } @@ -274,6 +691,107 @@ NULL)); } +/* creates a detached signature just like: + * `certtool --p7-detached-sign --load-certificate client.pem \ + * --load-privkey secret.pem --outfile=test.p7b` */ +static GBytes * +fu_keyring_pkcs7_sign_data (FuKeyring *keyring, + GBytes *blob, + FuKeyringSignFlags flags, + GError **error) +{ + FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring); + gnutls_datum_t d = { 0 }; + gnutls_digest_algorithm_t dig = GNUTLS_DIG_NULL; + guint gnutls_flags = 0; + int rc; + g_auto(gnutls_pkcs7_t) pkcs7 = NULL; + g_auto(gnutls_privkey_t) key = NULL; + g_auto(gnutls_pubkey_t) pubkey = NULL; + g_auto(gnutls_x509_crt_t) crt = NULL; + g_autoptr(gnutls_data_t) d_payload = NULL; + + /* ensure the client certificate exists */ + if (!fu_keyring_pkcs7_ensure_client_certificate (self, error)) { + g_prefix_error (error, "failed to generate client certificate: "); + return NULL; + } + + /* import the keys */ + crt = fu_keyring_pkcs7_load_client_certificate (self, error); + if (crt == NULL) + return NULL; + key = fu_keyring_pkcs7_load_privkey (self, error); + if (key == NULL) + return NULL; + + /* get the digest algorithm from the publix key */ + pubkey = fu_keyring_pkcs7_load_pubkey_from_privkey (key, error); + if (pubkey == NULL) + return NULL; + rc = gnutls_pubkey_get_preferred_hash_algorithm (pubkey, &dig, NULL); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "preferred_hash_algorithm: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; + } + + /* create container */ + rc = gnutls_pkcs7_init (&pkcs7); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "pkcs7_init: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; + } + + /* sign data */ + d.data = (unsigned char *) g_bytes_get_data (blob, NULL); + d.size = g_bytes_get_size (blob); + if (flags & FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP) + gnutls_flags |= GNUTLS_PKCS7_INCLUDE_TIME; + if (flags & FU_KEYRING_SIGN_FLAG_ADD_CERT) + gnutls_flags |= GNUTLS_PKCS7_INCLUDE_CERT; + rc = gnutls_pkcs7_sign (pkcs7, crt, key, &d, NULL, NULL, dig, gnutls_flags); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "pkcs7_sign: %s [%i]", + gnutls_strerror (rc), rc); + return NULL; + } + + /* set certificate */ + if (flags & FU_KEYRING_SIGN_FLAG_ADD_CERT) { + rc = gnutls_pkcs7_set_crt (pkcs7, crt); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "pkcs7_set_cr: %s", gnutls_strerror (rc)); + return NULL; + } + } + + /* export */ + rc = gnutls_pkcs7_export2 (pkcs7, GNUTLS_X509_FMT_PEM, &d); + if (rc < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "pkcs7_export: %s", gnutls_strerror (rc)); + return NULL; + } + d_payload = d.data; + return g_bytes_new (d_payload, d.size); +} + static void fu_keyring_pkcs7_finalize (GObject *object) { @@ -289,6 +807,7 @@ FuKeyringClass *klass_app = FU_KEYRING_CLASS (klass); klass_app->setup = fu_keyring_pkcs7_setup; klass_app->add_public_keys = fu_keyring_pkcs7_add_public_keys; + klass_app->sign_data = fu_keyring_pkcs7_sign_data; klass_app->verify_data = fu_keyring_pkcs7_verify_data; object_class->finalize = fu_keyring_pkcs7_finalize; } diff -Nru fwupd-1.0.9/src/fu-keyring-pkcs7.h fwupd-1.2.10/src/fu-keyring-pkcs7.h --- fwupd-1.0.9/src/fu-keyring-pkcs7.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-pkcs7.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_KEYRING_PKCS7_H -#define __FU_KEYRING_PKCS7_H +#pragma once #include "fu-keyring.h" @@ -19,5 +17,3 @@ FuKeyring *fu_keyring_pkcs7_new (void); G_END_DECLS - -#endif /* __FU_KEYRING_PKCS7_H */ diff -Nru fwupd-1.0.9/src/fu-keyring-result.c fwupd-1.2.10/src/fu-keyring-result.c --- fwupd-1.0.9/src/fu-keyring-result.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-result.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuKeyring" + #include "config.h" #include "fwupd-error.h" @@ -97,11 +98,15 @@ 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 -Nru fwupd-1.0.9/src/fu-keyring-result.h fwupd-1.2.10/src/fu-keyring-result.h --- fwupd-1.0.9/src/fu-keyring-result.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-result.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_KEYRING_RESULT_H -#define __FU_KEYRING_RESULT_H +#pragma once #include @@ -20,5 +18,3 @@ const gchar *fu_keyring_result_get_authority (FuKeyringResult *self); G_END_DECLS - -#endif /* __FU_KEYRING_RESULT_H */ diff -Nru fwupd-1.0.9/src/fu-keyring-utils.c fwupd-1.2.10/src/fu-keyring-utils.c --- fwupd-1.0.9/src/fu-keyring-utils.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-utils.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuKeyring" + #include #include "fwupd-error.h" @@ -65,26 +66,26 @@ } /** - * fu_keyring_get_release_trust_flags: - * @release: A #AsRelease, e.g. %FWUPD_KEYRING_KIND_GPG - * @trust_flags: A #FwupdTrustFlags, e.g. %FWUPD_TRUST_FLAG_PAYLOAD + * fu_keyring_get_release_flags: + * @release: A #XbNode, e.g. %FWUPD_KEYRING_KIND_GPG + * @flags: A #FwupdReleaseFlags, e.g. %FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD * @error: A #GError, or %NULL * * Uses the correct keyring to get the trust flags for a given release. * - * Returns: %TRUE if @trust_flags has been set + * Returns: %TRUE if @flags has been set **/ gboolean -fu_keyring_get_release_trust_flags (AsRelease *release, - FwupdTrustFlags *trust_flags, - GError **error) +fu_keyring_get_release_flags (XbNode *release, + FwupdReleaseFlags *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; @@ -99,28 +100,17 @@ { 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; @@ -132,7 +122,8 @@ } /* 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, @@ -144,6 +135,7 @@ /* 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, @@ -151,6 +143,7 @@ "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); @@ -166,7 +159,9 @@ fu_keyring_get_name (kr)); return FALSE; } - kr_result = fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local); + kr_result = fu_keyring_verify_data (kr, blob_payload, blob_signature, + FU_KEYRING_VERIFY_FLAG_NONE, + &error_local); if (kr_result == NULL) { g_warning ("untrusted as failed to verify from %s keyring: %s", fu_keyring_get_name (kr), @@ -176,6 +171,6 @@ /* awesome! */ g_debug ("marking payload as trusted"); - *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD; + *flags |= FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD; return TRUE; } diff -Nru fwupd-1.0.9/src/fu-keyring-utils.h fwupd-1.2.10/src/fu-keyring-utils.h --- fwupd-1.0.9/src/fu-keyring-utils.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-keyring-utils.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,22 +1,22 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_KEYRING_UTILS_H__ -#define __FU_KEYRING_UTILS_H__ +#pragma once -#include +#include #include "fu-keyring.h" #include "fwupd-enums.h" +G_BEGIN_DECLS + FuKeyring *fu_keyring_create_for_kind (FwupdKeyringKind kind, GError **error); -gboolean fu_keyring_get_release_trust_flags (AsRelease *release, - FwupdTrustFlags *trust_flags, +gboolean fu_keyring_get_release_flags (XbNode *release, + FwupdReleaseFlags *flags, GError **error); -#endif /* __FU_KEYRING_UTILS_H__ */ +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-main.c fwupd-1.2.10/src/fu-main.c --- fwupd-1.0.9/src/fu-main.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-main.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,16 +1,18 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuMain" + #include "config.h" -#include +#include #include #include #include +#include #include #include #include @@ -39,11 +41,27 @@ GDBusNodeInfo *introspection_daemon; GDBusProxy *proxy_uid; GMainLoop *loop; + GFileMonitor *argv0_monitor; 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) { @@ -124,12 +142,14 @@ GVariantBuilder invalidated_builder; /* not yet connected */ - if (priv->connection == NULL) + if (priv->connection == NULL) { + g_variant_unref (g_variant_ref_sink (property_value)); return; + } /* build the dict */ g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as")); - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&builder, "{sv}", property_name, @@ -163,6 +183,10 @@ 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 @@ -175,15 +199,51 @@ g_variant_new_uint32 (percentage)); } +static gboolean +fu_main_get_device_flags_for_sender (FuMainPrivate *priv, const char *sender, + FwupdDeviceFlags *flags, GError **error) +{ + uid_t calling_uid; + g_autoptr(GVariant) value = NULL; + + g_return_val_if_fail (sender != NULL, FALSE); + g_return_val_if_fail (flags != NULL, FALSE); + + value = g_dbus_proxy_call_sync (priv->proxy_uid, + "GetConnectionUnixUser", + g_variant_new ("(s)", sender), + G_DBUS_CALL_FLAGS_NONE, + 2000, + NULL, + error); + if (value == NULL) { + g_prefix_error (error, "failed to read user id of caller: "); + return FALSE; + } + g_variant_get (value, "(u)", &calling_uid); + if (calling_uid == 0) + *flags |= FWUPD_DEVICE_FLAG_TRUSTED; + + return TRUE; +} + static GVariant * -fu_main_device_array_to_variant (GPtrArray *devices) +fu_main_device_array_to_variant (FuMainPrivate *priv, const gchar *sender, + GPtrArray *devices, GError **error) { GVariantBuilder builder; + FwupdDeviceFlags flags = FWUPD_DEVICE_FLAG_NONE; + g_return_val_if_fail (devices->len > 0, NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + + if (!fu_main_get_device_flags_for_sender (priv, sender, &flags, error)) + return NULL; + for (guint i = 0; i < devices->len; i++) { FuDevice *device = g_ptr_array_index (devices, i); - GVariant *tmp = fwupd_device_to_variant (FWUPD_DEVICE (device)); + GVariant *tmp = fwupd_device_to_variant_full (FWUPD_DEVICE (device), + flags); g_variant_builder_add_value (&builder, tmp); } return g_variant_new ("(aa{sv})", &builder); @@ -236,13 +296,15 @@ PolkitSubject *subject; GPtrArray *install_tasks; GPtrArray *action_ids; - FwupdInstallFlags flags; + GPtrArray *checksums; + guint64 flags; GBytes *blob_cab; FuMainPrivate *priv; gchar *device_id; gchar *remote_id; gchar *key; gchar *value; + XbSilo *silo; } FuMainAuthHelper; static void @@ -252,10 +314,14 @@ 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) g_ptr_array_unref (helper->action_ids); + if (helper->checksums != NULL) + g_ptr_array_unref (helper->checksums); g_free (helper->device_id); g_free (helper->remote_id); g_free (helper->key); @@ -325,6 +391,108 @@ } static void +fu_main_authorize_set_approved_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; + g_autoptr(GError) error = NULL; + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE); + auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), + res, &error); + if (!fu_main_authorization_is_valid (auth, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + /* success */ + for (guint i = 0; i < helper->checksums->len; i++) { + const gchar *csum = g_ptr_array_index (helper->checksums, i); + fu_engine_add_approved_firmware (helper->priv->engine, csum); + } + g_dbus_method_invocation_return_value (helper->invocation, NULL); +} + +static void +fu_main_authorize_self_sign_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; + g_autofree gchar *sig = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE); + auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), + res, &error); + if (!fu_main_authorization_is_valid (auth, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + /* authenticated */ + sig = fu_engine_self_sign (helper->priv->engine, helper->value, helper->flags, &error); + if (sig == NULL) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value (helper->invocation, g_variant_new ("(s)", sig)); +} + +static void +fu_main_modify_config_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; + g_autoptr(GError) error = NULL; + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), + res, &error); + if (!fu_main_authorization_is_valid (auth, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + if (!fu_engine_modify_config (helper->priv->engine, helper->key, helper->value, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value (helper->invocation, NULL); +} + +static void +fu_main_authorize_activate_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; + g_autoptr(GError) error = NULL; + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE); + auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), + res, &error); + if (!fu_main_authorization_is_valid (auth, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + /* authenticated */ + if (!fu_engine_activate (helper->priv->engine, helper->device_id, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value (helper->invocation, NULL); +} + +static void fu_main_authorize_verify_update_cb (GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; @@ -408,6 +576,7 @@ 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) { @@ -424,11 +593,16 @@ } /* 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; } @@ -462,47 +636,83 @@ return fu_install_task_compare (task_a, task_b); } +static GPtrArray * +fu_main_get_device_family (FuMainAuthHelper *helper, GError **error) +{ + FuDevice *parent; + GPtrArray *children; + g_autoptr(FuDevice) device = NULL; + g_autoptr(GPtrArray) devices_possible = NULL; + + /* get the device */ + device = fu_engine_get_device (helper->priv->engine, helper->device_id, error); + if (device == NULL) + return NULL; + + /* device itself */ + devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_ptr_array_add (devices_possible, g_object_ref (device)); + + /* add device children */ + children = fu_device_get_children (device); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + g_ptr_array_add (devices_possible, g_object_ref (child)); + } + + /* add parent and siblings, not including the device itself */ + parent = fu_device_get_parent (device); + if (parent != NULL) { + GPtrArray *siblings = fu_device_get_children (parent); + g_ptr_array_add (devices_possible, g_object_ref (parent)); + for (guint i = 0; i < siblings->len; i++) { + FuDevice *sibling = g_ptr_array_index (siblings, i); + if (sibling == device) + continue; + g_ptr_array_add (devices_possible, g_object_ref (sibling)); + } + } + + /* success */ + return g_steal_pointer (&devices_possible); +} + 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; - /* get a list of devices that match the device_id */ + /* get a list of devices that in some way match the device_id */ if (g_strcmp0 (helper->device_id, FWUPD_DEVICE_ID_ANY) == 0) { devices_possible = fu_engine_get_devices (priv->engine, error); - if (devices_possible == NULL) { - g_prefix_error (error, "failed to get all devices: "); + if (devices_possible == NULL) return FALSE; - } } else { - FuDevice *device = fu_engine_get_device (priv->engine, - helper->device_id, - error); - if (device == NULL) + devices_possible = fu_main_get_device_family (helper, error); + if (devices_possible == NULL) return FALSE; - devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - g_ptr_array_add (devices_possible, device); } - /* 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++) { @@ -512,19 +722,22 @@ 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)) @@ -574,6 +787,9 @@ 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); @@ -582,7 +798,11 @@ g_dbus_method_invocation_return_gerror (invocation, error); return; } - val = fu_main_device_array_to_variant (devices); + val = fu_main_device_array_to_variant (priv, sender, devices, &error); + if (val == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } g_dbus_method_invocation_return_value (invocation, val); return; } @@ -604,6 +824,86 @@ g_dbus_method_invocation_return_value (invocation, val); return; } + if (g_strcmp0 (method_name, "GetApprovedFirmware") == 0) { + GVariantBuilder builder; + GPtrArray *checksums = fu_engine_get_approved_firmware (priv->engine); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + for (guint i = 0; i < checksums->len; i++) { + const gchar *checksum = g_ptr_array_index (checksums, i); + g_variant_builder_add_value (&builder, g_variant_new_string (checksum)); + } + val = g_variant_builder_end (&builder); + g_dbus_method_invocation_return_value (invocation, + g_variant_new_tuple (&val, 1)); + return; + } + if (g_strcmp0 (method_name, "SetApprovedFirmware") == 0) { + g_autofree gchar *checksums_str = NULL; + g_auto(GStrv) checksums = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; + g_autoptr(PolkitSubject) subject = NULL; + + g_variant_get (parameters, "(^as)", &checksums); + checksums_str = g_strjoinv (",", checksums); + g_debug ("Called %s(%s)", method_name, checksums_str); + + /* authenticate */ + fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0 (FuMainAuthHelper, 1); + helper->priv = priv; + helper->invocation = g_object_ref (invocation); + helper->checksums = g_ptr_array_new_with_free_func (g_free); + for (guint i = 0; checksums[i] != NULL; i++) + g_ptr_array_add (helper->checksums, g_strdup (checksums[i])); + subject = polkit_system_bus_name_new (sender); + polkit_authority_check_authorization (priv->authority, subject, + "org.freedesktop.fwupd.set-approved-firmware", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_main_authorize_set_approved_firmware_cb, + g_steal_pointer (&helper)); + return; + } + if (g_strcmp0 (method_name, "SelfSign") == 0) { + GVariant *prop_value; + gchar *prop_key; + g_autofree gchar *value = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; + g_autoptr(PolkitSubject) subject = NULL; + g_autoptr(GVariantIter) iter = NULL; + + g_variant_get (parameters, "(sa{sv})", &value, &iter); + g_debug ("Called %s(%s)", method_name, value); + + /* get flags */ + helper = g_new0 (FuMainAuthHelper, 1); + while (g_variant_iter_next (iter, "{&sv}", &prop_key, &prop_value)) { + g_debug ("got option %s", prop_key); + if (g_strcmp0 (prop_key, "add-timestamp") == 0 && + g_variant_get_boolean (prop_value) == TRUE) + helper->flags |= FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP; + if (g_strcmp0 (prop_key, "add-cert") == 0 && + g_variant_get_boolean (prop_value) == TRUE) + helper->flags |= FU_KEYRING_SIGN_FLAG_ADD_CERT; + g_variant_unref (prop_value); + } + + /* authenticate */ + fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH); + helper->priv = priv; + helper->value = g_steal_pointer (&value); + helper->invocation = g_object_ref (invocation); + subject = polkit_system_bus_name_new (sender); + polkit_authority_check_authorization (priv->authority, subject, + "org.freedesktop.fwupd.self-sign", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_main_authorize_self_sign_cb, + g_steal_pointer (&helper)); + return; + } if (g_strcmp0 (method_name, "GetDowngrades") == 0) { const gchar *device_id; g_autoptr(GPtrArray) releases = NULL; @@ -660,7 +960,11 @@ g_dbus_method_invocation_return_gerror (invocation, error); return; } - val = fu_main_device_array_to_variant (devices); + val = fu_main_device_array_to_variant (priv, sender, devices, &error); + if (val == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } g_dbus_method_invocation_return_value (invocation, val); return; } @@ -779,6 +1083,59 @@ g_steal_pointer (&helper)); return; } + if (g_strcmp0 (method_name, "Activate") == 0) { + const gchar *device_id = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; + g_autoptr(PolkitSubject) subject = NULL; + + g_variant_get (parameters, "(&s)", &device_id); + g_debug ("Called %s(%s)", method_name, device_id); + if (!fu_main_device_id_valid (device_id, &error)) { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } + + /* authenticate */ + fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0 (FuMainAuthHelper, 1); + helper->priv = priv; + helper->invocation = g_object_ref (invocation); + helper->device_id = g_strdup (device_id); + subject = polkit_system_bus_name_new (sender); + polkit_authority_check_authorization (priv->authority, subject, + "org.freedesktop.fwupd.device-activate", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_main_authorize_activate_cb, + g_steal_pointer (&helper)); + return; + } + if (g_strcmp0 (method_name, "ModifyConfig") == 0) { + g_autofree gchar *key = NULL; + g_autofree gchar *value = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; + g_autoptr(PolkitSubject) subject = NULL; + + g_variant_get (parameters, "(ss)", &key, &value); + g_debug ("Called %s(%s=%s)", method_name, key, value); + + /* authenticate */ + helper = g_new0 (FuMainAuthHelper, 1); + helper->priv = priv; + helper->key = g_steal_pointer (&key); + helper->value = g_steal_pointer (&value); + helper->invocation = g_object_ref (invocation); + subject = polkit_system_bus_name_new (sender); + polkit_authority_check_authorization (priv->authority, subject, + "org.freedesktop.fwupd.modify-config", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_main_modify_config_cb, + g_steal_pointer (&helper)); + return; + } if (g_strcmp0 (method_name, "ModifyRemote") == 0) { const gchar *remote_id = NULL; const gchar *key = NULL; @@ -994,9 +1351,15 @@ { 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)); @@ -1048,10 +1411,6 @@ g_warning ("cannot connect to DBus: %s", error->message); return; } - - /* dump startup profile data */ - if (g_getenv ("FWUPD_VERBOSE") != NULL) - fu_engine_profile_dump (priv->engine); } static void @@ -1080,6 +1439,21 @@ return G_SOURCE_REMOVE; } +static void +fu_main_argv_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, + GFileMonitorEvent event_type, gpointer user_data) +{ + FuMainPrivate *priv = (FuMainPrivate *) user_data; + + /* can do straight away? */ + if (priv->update_in_progress) { + g_warning ("binary changed during a firmware update, ignoring"); + return; + } + g_debug ("binary changed, shutting down"); + g_main_loop_quit (priv->loop); +} + static GDBusNodeInfo * fu_main_load_introspection (const gchar *filename, GError **error) { @@ -1099,31 +1473,6 @@ return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error); } -static gboolean -fu_main_perhaps_own_name (gpointer user_data) -{ - FuMainPrivate *priv = (FuMainPrivate *) user_data; - g_autoptr(GError) error = NULL; - - /* are any plugins pending */ - if (!fu_engine_check_plugins_pending (priv->engine, &error)) { - g_debug ("trying again: %s", error->message); - return G_SOURCE_CONTINUE; - } - - /* own the object */ - g_debug ("registering D-Bus service"); - priv->owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, - FWUPD_DBUS_SERVICE, - G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | - G_BUS_NAME_OWNER_FLAGS_REPLACE, - fu_main_on_bus_acquired_cb, - fu_main_on_name_acquired_cb, - fu_main_on_name_lost_cb, - priv, NULL); - return G_SOURCE_REMOVE; -} - static void fu_main_private_free (FuMainPrivate *priv) { @@ -1139,6 +1488,8 @@ g_object_unref (priv->connection); if (priv->authority != NULL) g_object_unref (priv->authority); + if (priv->argv0_monitor != NULL) + g_object_unref (priv->argv0_monitor); if (priv->introspection_daemon != NULL) g_dbus_node_info_unref (priv->introspection_daemon); g_free (priv); @@ -1165,6 +1516,7 @@ }; g_autoptr(FuMainPrivate) priv = NULL; g_autoptr(GError) error = NULL; + g_autoptr(GFile) argv0_file = g_file_new_for_path (argv[0]); g_autoptr(GOptionContext) context = NULL; setlocale (LC_ALL, ""); @@ -1209,13 +1561,20 @@ g_signal_connect (priv->engine, "percentage-changed", G_CALLBACK (fu_main_engine_percentage_changed_cb), priv); - if (!fu_engine_load (priv->engine, &error)) { + if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NONE, &error)) { g_printerr ("Failed to load engine: %s\n", error->message); return EXIT_FAILURE; } - /* keep polling until all the plugins are ready */ - g_timeout_add (200, fu_main_perhaps_own_name, priv); + g_unix_signal_add_full (G_PRIORITY_DEFAULT, + SIGTERM, fu_main_sigterm_cb, + priv, NULL); + + /* restart the daemon if the binary gets replaced */ + priv->argv0_monitor = g_file_monitor_file (argv0_file, G_FILE_MONITOR_NONE, + NULL, &error); + g_signal_connect (priv->argv0_monitor, "changed", + G_CALLBACK (fu_main_argv_changed_cb), priv); /* load introspection from file */ priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml", @@ -1232,6 +1591,16 @@ return EXIT_FAILURE; } + /* own the object */ + priv->owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + FWUPD_DBUS_SERVICE, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE, + fu_main_on_bus_acquired_cb, + fu_main_on_name_acquired_cb, + fu_main_on_name_lost_cb, + priv, NULL); + /* Only timeout and close the mainloop if we have specified it * on the command line */ if (immediate_exit) @@ -1239,6 +1608,8 @@ 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 -Nru fwupd-1.0.9/src/fu-mutex.h fwupd-1.2.10/src/fu-mutex.h --- fwupd-1.0.9/src/fu-mutex.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-mutex.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2019 Kalev Lember + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#if !GLIB_CHECK_VERSION(2, 61, 1) + +/* Backported GRWLock autoptr support for older glib versions */ + +typedef void GRWLockWriterLocker; + +static inline GRWLockWriterLocker * +g_rw_lock_writer_locker_new (GRWLock *rw_lock) +{ + g_rw_lock_writer_lock (rw_lock); + return (GRWLockWriterLocker *) rw_lock; +} + +static inline void +g_rw_lock_writer_locker_free (GRWLockWriterLocker *locker) +{ + g_rw_lock_writer_unlock ((GRWLock *) locker); +} + +typedef void GRWLockReaderLocker; + +static inline GRWLockReaderLocker * +g_rw_lock_reader_locker_new (GRWLock *rw_lock) +{ + g_rw_lock_reader_lock (rw_lock); + return (GRWLockReaderLocker *) rw_lock; +} + +static inline void +g_rw_lock_reader_locker_free (GRWLockReaderLocker *locker) +{ + g_rw_lock_reader_unlock ((GRWLock *) locker); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRWLockWriterLocker, g_rw_lock_writer_locker_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRWLockReaderLocker, g_rw_lock_reader_locker_free) + +#endif + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-offline.c fwupd-1.2.10/src/fu-offline.c --- fwupd-1.0.9/src/fu-offline.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-offline.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2015-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "fu-history.h" +#include "fu-plugin-private.h" +#include "fu-util-common.h" + +typedef enum { + FU_OFFLINE_FLAG_NONE = 0, + FU_OFFLINE_FLAG_ENABLE = 1 << 0, + FU_OFFLINE_FLAG_USE_PROGRESS = 1 << 1, +} FuOfflineFlag; + +struct FuUtilPrivate { + gchar *splash_cmd; + GTimer *splash_timer; + FuOfflineFlag splash_flags; +}; + +static gboolean +fu_offline_set_splash_progress (FuUtilPrivate *priv, guint percentage, GError **error) +{ + g_autofree gchar *str = g_strdup_printf ("%u", percentage); + const gchar *argv[] = { priv->splash_cmd, "system-update", "--progress", str, NULL }; + + /* call into plymouth if installed */ + if (priv->splash_flags == FU_OFFLINE_FLAG_NONE) { + /* TRANSLATORS: console message when not using plymouth */ + g_printerr ("%s: %u%%\n", _("Percentage complete"), percentage); + return TRUE; + } + + /* fall back to really old mode that should be supported by anything */ + if ((priv->splash_flags & FU_OFFLINE_FLAG_USE_PROGRESS) == 0) { + argv[1] = "display-message"; + argv[2] = "--text"; + } + return fu_common_spawn_sync (argv, NULL, NULL, 200, NULL, error); +} + +static gboolean +fu_offline_set_splash_mode (FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GError) error_local = NULL; + const gchar *argv[] = { priv->splash_cmd, "change-mode", "--system-upgrade", NULL }; + + /* call into plymouth if installed */ + if (priv->splash_cmd == NULL) { + /* TRANSLATORS: console message when no Plymouth is installed */ + g_printerr ("%s\n", _("Installing Firmware…")); + return TRUE; + } + + /* try the new fancy mode, then fall back to really old mode */ + if (!fu_common_spawn_sync (argv, NULL, NULL, 1500, NULL, &error_local)) { + argv[2] = "--updates"; + if (!fu_common_spawn_sync (argv, NULL, NULL, 1500, NULL, error)) { + g_prefix_error (error, "%s: ", error_local->message); + return FALSE; + } + priv->splash_flags = FU_OFFLINE_FLAG_ENABLE; + return TRUE; + } + + /* success */ + priv->splash_flags = FU_OFFLINE_FLAG_ENABLE | FU_OFFLINE_FLAG_USE_PROGRESS; + return TRUE; +} + +static gboolean +fu_offline_set_splash_reboot (FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GError) error_local = NULL; + const gchar *argv[] = { priv->splash_cmd, "change-mode", "--reboot", NULL }; + + /* call into plymouth if installed */ + if (priv->splash_flags == FU_OFFLINE_FLAG_NONE) { + /* TRANSLATORS: console message when not using plymouth */ + g_printerr ("%s\n", _("Rebooting…")); + return TRUE; + } + + /* try the new fancy mode, then fall back to really old mode */ + if (!fu_common_spawn_sync (argv, NULL, NULL, 200, NULL, &error_local)) { + /* fall back to really old mode that should be supported */ + argv[2] = "--shutdown"; + if (!fu_common_spawn_sync (argv, NULL, NULL, 200, NULL, error)) { + g_prefix_error (error, "%s: ", error_local->message); + return FALSE; + } + return TRUE; + } + + /* success */ + return TRUE; +} + +static void +fu_offline_client_notify_cb (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + FuUtilPrivate *priv = (FuUtilPrivate *) user_data; + FwupdClient *client = FWUPD_CLIENT (object); + + /* rate limit to 1 second */ + if (g_timer_elapsed (priv->splash_timer, NULL) < 1.f || + fwupd_client_get_percentage (client) < 5) + return; + fu_offline_set_splash_progress (priv, fwupd_client_get_percentage (client), NULL); + g_timer_reset (priv->splash_timer); +} + +static void +fu_util_private_free (FuUtilPrivate *priv) +{ + if (priv->splash_timer != NULL) + g_timer_destroy (priv->splash_timer); + g_free (priv->splash_cmd); + g_free (priv); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) +#pragma clang diagnostic pop + +int +main (int argc, char *argv[]) +{ + gint vercmp; + guint cnt = 0; + g_autofree gchar *link = NULL; + g_autofree gchar *target = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + g_autoptr(FuHistory) history = NULL; + g_autoptr(FwupdClient) client = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) results = NULL; + g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); + + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + /* verify this is pointing to our cache */ + link = g_file_read_link (FU_OFFLINE_TRIGGER_FILENAME, NULL); + if (link == NULL) + return EXIT_SUCCESS; + if (g_strcmp0 (link, target) != 0) + return EXIT_SUCCESS; + + /* do this first to avoid a loop if this tool segfaults */ + g_unlink (FU_OFFLINE_TRIGGER_FILENAME); + + /* ensure root user */ + if (getuid () != 0 || geteuid () != 0) { + /* TRANSLATORS: the user needs to stop playing with stuff */ + g_printerr ("%s\n", _("This tool can only be used by the root user")); + return EXIT_FAILURE; + } + + /* find plymouth, but not an error if not found */ + priv->splash_cmd = g_find_program_in_path ("plymouth"); + priv->splash_timer = g_timer_new (); + + /* ensure D-Bus errors are registered */ + fwupd_error_quark (); + + /* get prepared updates */ + history = fu_history_new (); + results = fu_history_get_devices (history, &error); + if (results == NULL) { + /* TRANSLATORS: we could not get the devices to update offline */ + g_printerr ("%s: %s\n", _("Failed to get pending devices"), + error->message); + return EXIT_FAILURE; + } + + /* connect to the daemon */ + client = fwupd_client_new (); + g_signal_connect (client, "notify::percentage", + G_CALLBACK (fu_offline_client_notify_cb), priv); + if (!fwupd_client_connect (client, NULL, &error)) { + /* TRANSLATORS: we could not talk to the fwupd daemon */ + g_printerr ("%s: %s\n", _("Failed to connect to daemon"), + error->message); + return EXIT_FAILURE; + } + + /* set up splash */ + if (!fu_offline_set_splash_mode (priv, &error)) { + /* TRANSLATORS: we could not talk to plymouth */ + g_printerr ("%s: %s\n", _("Failed to set splash mode"), + error->message); + return EXIT_FAILURE; + } + + /* apply each update */ + for (guint i = 0; i < results->len; i++) { + FwupdDevice *dev = g_ptr_array_index (results, i); + FwupdRelease *rel = fwupd_device_get_release_default (dev); + + /* check not already done */ + if (fwupd_device_get_update_state (dev) != FWUPD_UPDATE_STATE_PENDING) + continue; + + /* tell the user what's going to happen */ + 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 + * e.g. "1.2.3" */ + g_print (_("Reinstalling %s with %s... "), + fwupd_device_get_name (dev), + fwupd_release_get_version (rel)); + } else if (vercmp > 0) { + /* TRANSLATORS: the first replacement is a display name + * e.g. "ColorHugALS" and the second and third are + * version numbers e.g. "1.2.3" */ + g_print (_("Downgrading %s from %s to %s... "), + fwupd_device_get_name (dev), + fwupd_device_get_version (dev), + fwupd_release_get_version (rel)); + } else if (vercmp < 0) { + /* TRANSLATORS: the first replacement is a display name + * e.g. "ColorHugALS" and the second and third are + * version numbers e.g. "1.2.3" */ + g_print (_("Updating %s from %s to %s... "), + fwupd_device_get_name (dev), + fwupd_device_get_version (dev), + fwupd_release_get_version (rel)); + } + if (!fwupd_client_install (client, + fwupd_device_get_id (dev), + fwupd_release_get_filename (rel), + FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | + FWUPD_INSTALL_FLAG_ALLOW_OLDER | + FWUPD_INSTALL_FLAG_OFFLINE, + NULL, + &error)) { + /* TRANSLATORS: we could not install for some reason */ + g_printerr ("%s: %s\n", _("Failed to install firmware update"), + error->message); + return EXIT_FAILURE; + } + cnt++; + } + + /* nothing to do */ + if (cnt == 0) { + /* TRANSLATORS: nothing was updated offline */ + g_printerr ("%s\n", _("No updates were applied")); + return EXIT_FAILURE; + } + + /* reboot */ + fu_offline_set_splash_reboot (priv, NULL); + if (!fu_util_update_reboot (&error)) { + /* TRANSLATORS: we could not reboot for some reason */ + g_printerr ("%s: %s\n", _("Failed to reboot"), error->message); + return EXIT_FAILURE; + } + + /* success */ + g_print ("%s\n", _("Done!")); + return EXIT_SUCCESS; +} diff -Nru fwupd-1.0.9/src/fu-plugin.c fwupd-1.2.10/src/fu-plugin.c --- fwupd-1.0.9/src/fu-plugin.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-plugin.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,17 +1,18 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuPlugin" + #include "config.h" #include #include -#include #include #include +#include #include #ifdef HAVE_VALGRIND #include @@ -20,6 +21,7 @@ #include "fu-device-private.h" #include "fu-plugin-private.h" #include "fu-history.h" +#include "fu-mutex.h" /** * SECTION:fu-plugin @@ -39,16 +41,18 @@ GUsbContext *usb_ctx; gboolean enabled; guint order; + 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 */ - GHashTable *devices_delay; /* FuDevice:FuPluginHelper */ + GRWLock devices_mutex; GHashTable *report_metadata; /* key:value */ FuPluginData *data; } FuPluginPrivate; @@ -57,8 +61,10 @@ SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_REMOVED, SIGNAL_DEVICE_REGISTER, + SIGNAL_RULES_CHANGED, SIGNAL_RECOLDPLUG, SIGNAL_SET_COLDPLUG_DELAY, + SIGNAL_CHECK_SUPPORTED, SIGNAL_LAST }; @@ -68,33 +74,40 @@ #define GET_PRIVATE(o) (fu_plugin_get_instance_private (o)) typedef const gchar *(*FuPluginGetNameFunc) (void); -typedef void (*FuPluginInitFunc) (FuPlugin *plugin); -typedef gboolean (*FuPluginStartupFunc) (FuPlugin *plugin, +typedef void (*FuPluginInitFunc) (FuPlugin *self); +typedef gboolean (*FuPluginStartupFunc) (FuPlugin *self, GError **error); -typedef void (*FuPluginDeviceRegisterFunc) (FuPlugin *plugin, +typedef void (*FuPluginDeviceRegisterFunc) (FuPlugin *self, FuDevice *device); -typedef gboolean (*FuPluginDeviceFunc) (FuPlugin *plugin, +typedef gboolean (*FuPluginDeviceFunc) (FuPlugin *self, + FuDevice *device, + GError **error); +typedef gboolean (*FuPluginFlaggedDeviceFunc) (FuPlugin *self, + FwupdInstallFlags flags, FuDevice *device, GError **error); -typedef gboolean (*FuPluginDeviceArrayFunc) (FuPlugin *plugin, +typedef gboolean (*FuPluginDeviceArrayFunc) (FuPlugin *self, GPtrArray *devices, GError **error); -typedef gboolean (*FuPluginVerifyFunc) (FuPlugin *plugin, +typedef gboolean (*FuPluginVerifyFunc) (FuPlugin *self, FuDevice *device, FuPluginVerifyFlags flags, GError **error); -typedef gboolean (*FuPluginUpdateFunc) (FuPlugin *plugin, +typedef gboolean (*FuPluginUpdateFunc) (FuPlugin *self, FuDevice *device, GBytes *blob_fw, FwupdInstallFlags flags, GError **error); -typedef gboolean (*FuPluginUsbDeviceAddedFunc) (FuPlugin *plugin, - GUsbDevice *usb_device, +typedef gboolean (*FuPluginUsbDeviceAddedFunc) (FuPlugin *self, + FuUsbDevice *device, + GError **error); +typedef gboolean (*FuPluginUdevDeviceAddedFunc) (FuPlugin *self, + FuUdevDevice *device, GError **error); /** * fu_plugin_get_name: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * * Gets the plugin name. * @@ -103,26 +116,54 @@ * Since: 0.8.0 **/ const gchar * -fu_plugin_get_name (FuPlugin *plugin) +fu_plugin_get_name (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); return priv->name; } void -fu_plugin_set_name (FuPlugin *plugin, const gchar *name) +fu_plugin_set_name (FuPlugin *self, const gchar *name) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_if_fail (FU_IS_PLUGIN (plugin)); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (name != NULL); g_free (priv->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: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @id: the key * * Finds an object in the per-plugin cache. @@ -132,17 +173,19 @@ * Since: 0.8.0 **/ gpointer -fu_plugin_cache_lookup (FuPlugin *plugin, const gchar *id) +fu_plugin_cache_lookup (FuPlugin *self, const gchar *id) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->devices_mutex); + g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); g_return_val_if_fail (id != NULL, NULL); + g_return_val_if_fail (locker != NULL, NULL); return g_hash_table_lookup (priv->devices, id); } /** * fu_plugin_cache_add: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @id: the key * @dev: a #GObject, typically a #FuDevice * @@ -151,17 +194,19 @@ * Since: 0.8.0 **/ void -fu_plugin_cache_add (FuPlugin *plugin, const gchar *id, gpointer dev) +fu_plugin_cache_add (FuPlugin *self, const gchar *id, gpointer dev) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_if_fail (FU_IS_PLUGIN (plugin)); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_writer_locker_new (&priv->devices_mutex); + g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (id != NULL); + g_return_if_fail (locker != NULL); g_hash_table_insert (priv->devices, g_strdup (id), g_object_ref (dev)); } /** * fu_plugin_cache_remove: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @id: the key * * Removes an object from the per-plugin cache. @@ -169,17 +214,19 @@ * Since: 0.8.0 **/ void -fu_plugin_cache_remove (FuPlugin *plugin, const gchar *id) +fu_plugin_cache_remove (FuPlugin *self, const gchar *id) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_if_fail (FU_IS_PLUGIN (plugin)); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_writer_locker_new (&priv->devices_mutex); + g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (id != NULL); + g_return_if_fail (locker != NULL); g_hash_table_remove (priv->devices, id); } /** * fu_plugin_get_data: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * * Gets the per-plugin allocated private data. This will return %NULL unless * fu_plugin_alloc_data() has been called by the plugin. @@ -189,16 +236,16 @@ * Since: 0.8.0 **/ FuPluginData * -fu_plugin_get_data (FuPlugin *plugin) +fu_plugin_get_data (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); return priv->data; } /** * fu_plugin_alloc_data: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @data_sz: the size to allocate * * Allocates the per-plugin allocated private data. @@ -208,10 +255,10 @@ * Since: 0.8.0 **/ FuPluginData * -fu_plugin_alloc_data (FuPlugin *plugin, gsize data_sz) +fu_plugin_alloc_data (FuPlugin *self, gsize data_sz) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); if (priv->data != NULL) { g_critical ("fu_plugin_alloc_data() already used by plugin"); return priv->data; @@ -222,7 +269,7 @@ /** * fu_plugin_get_usb_context: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * * Gets the shared USB context that all plugins can use. * @@ -231,23 +278,23 @@ * Since: 0.8.0 **/ GUsbContext * -fu_plugin_get_usb_context (FuPlugin *plugin) +fu_plugin_get_usb_context (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); return priv->usb_ctx; } void -fu_plugin_set_usb_context (FuPlugin *plugin, GUsbContext *usb_ctx) +fu_plugin_set_usb_context (FuPlugin *self, GUsbContext *usb_ctx) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); g_set_object (&priv->usb_ctx, usb_ctx); } /** * fu_plugin_get_enabled: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * * Returns if the plugin is enabled. Plugins may self-disable using * fu_plugin_set_enabled() or can be disabled by the daemon. @@ -257,16 +304,16 @@ * Since: 0.8.0 **/ gboolean -fu_plugin_get_enabled (FuPlugin *plugin) +fu_plugin_get_enabled (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), FALSE); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_PLUGIN (self), FALSE); return priv->enabled; } /** * fu_plugin_set_enabled: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @enabled: the enabled value * * Enables or disables a plugin. Plugins can self-disable at any point. @@ -274,10 +321,10 @@ * Since: 0.8.0 **/ void -fu_plugin_set_enabled (FuPlugin *plugin, gboolean enabled) +fu_plugin_set_enabled (FuPlugin *self, gboolean enabled) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_if_fail (FU_IS_PLUGIN (plugin)); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_PLUGIN (self)); priv->enabled = enabled; } @@ -295,9 +342,9 @@ } gboolean -fu_plugin_open (FuPlugin *plugin, const gchar *filename, GError **error) +fu_plugin_open (FuPlugin *self, const gchar *filename, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginInitFunc func = NULL; priv->module = g_module_open (filename, 0); @@ -318,7 +365,7 @@ g_module_symbol (priv->module, "fu_plugin_init", (gpointer *) &func); if (func != NULL) { g_debug ("performing init() on %s", filename); - func (plugin); + func (self); } return TRUE; @@ -326,7 +373,7 @@ /** * fu_plugin_device_add: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @device: A #FuDevice * * Asks the daemon to add a device to the exported list. If this device ID @@ -340,31 +387,39 @@ * Since: 0.8.0 **/ void -fu_plugin_device_add (FuPlugin *plugin, FuDevice *device) +fu_plugin_device_add (FuPlugin *self, FuDevice *device) { GPtrArray *children; + g_autoptr(GError) error = NULL; - g_return_if_fail (FU_IS_PLUGIN (plugin)); + g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (FU_IS_DEVICE (device)); + /* ensure the device ID is set from the physical and logical IDs */ + if (!fu_device_ensure_id (device, &error)) { + g_warning ("ignoring add: %s", error->message); + return; + } + g_debug ("emit added from %s: %s", - fu_plugin_get_name (plugin), + fu_plugin_get_name (self), fu_device_get_id (device)); fu_device_set_created (device, (guint64) g_get_real_time () / G_USEC_PER_SEC); - fu_device_set_plugin (device, fu_plugin_get_name (plugin)); - g_signal_emit (plugin, signals[SIGNAL_DEVICE_ADDED], 0, device); + fu_device_set_plugin (device, fu_plugin_get_name (self)); + g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device); - /* add children */ + /* add children if they have not already been added */ children = fu_device_get_children (device); for (guint i = 0; i < children->len; i++) { FuDevice *child = g_ptr_array_index (children, i); - fu_plugin_device_add (plugin, child); + if (fu_device_get_created (child) == 0) + fu_plugin_device_add (self, child); } } /** * fu_plugin_device_register: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @device: A #FuDevice * * Registers the device with other plugins so they can set metadata. @@ -377,99 +432,28 @@ * Since: 0.9.7 **/ void -fu_plugin_device_register (FuPlugin *plugin, FuDevice *device) -{ - g_return_if_fail (FU_IS_PLUGIN (plugin)); - g_return_if_fail (FU_IS_DEVICE (device)); - - g_debug ("emit device-register from %s: %s", - fu_plugin_get_name (plugin), - fu_device_get_id (device)); - g_signal_emit (plugin, signals[SIGNAL_DEVICE_REGISTER], 0, device); -} - -typedef struct { - FuPlugin *plugin; - FuDevice *device; - guint timeout_id; - GHashTable *devices; -} FuPluginHelper; - -static void -fu_plugin_helper_free (FuPluginHelper *helper) -{ - g_object_unref (helper->plugin); - g_object_unref (helper->device); - g_hash_table_unref (helper->devices); - g_free (helper); -} - -static gboolean -fu_plugin_device_add_delay_cb (gpointer user_data) -{ - FuPluginHelper *helper = (FuPluginHelper *) user_data; - fu_plugin_device_add (helper->plugin, helper->device); - g_hash_table_remove (helper->devices, helper->device); - fu_plugin_helper_free (helper); - return FALSE; -} - -/** - * fu_plugin_has_device_delay: - * @plugin: A #FuPlugin - * - * Returns if the device has a pending device that is waiting to be added. - * - * Returns: %TRUE if a device is waiting to be added - * - * Since: 0.8.0 - **/ -gboolean -fu_plugin_has_device_delay (FuPlugin *plugin) -{ - FuPluginPrivate *priv = GET_PRIVATE (plugin); - return g_hash_table_size (priv->devices_delay) > 0; -} - -/** - * fu_plugin_device_add_delay: - * @plugin: A #FuPlugin - * @device: A #FuDevice - * - * Asks the daemon to add a device to the exported list after a small delay. - * - * Since: 0.8.0 - **/ -void -fu_plugin_device_add_delay (FuPlugin *plugin, FuDevice *device) +fu_plugin_device_register (FuPlugin *self, FuDevice *device) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - FuPluginHelper *helper; + g_autoptr(GError) error = NULL; - g_return_if_fail (FU_IS_PLUGIN (plugin)); + g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (FU_IS_DEVICE (device)); - /* already waiting for add */ - helper = g_hash_table_lookup (priv->devices_delay, device); - if (helper != NULL) { - g_debug ("ignoring add-delay as device %s already pending", - fu_device_get_id (device)); + /* ensure the device ID is set from the physical and logical IDs */ + if (!fu_device_ensure_id (device, &error)) { + g_warning ("ignoring registration: %s", error->message); return; } - /* add after a small delay */ - g_debug ("waiting a small time for other plugins"); - helper = g_new0 (FuPluginHelper, 1); - helper->plugin = g_object_ref (plugin); - helper->device = g_object_ref (device); - helper->timeout_id = g_timeout_add (500, fu_plugin_device_add_delay_cb, helper); - helper->devices = g_hash_table_ref (priv->devices_delay); - g_hash_table_insert (helper->devices, device, helper); + g_debug ("emit device-register from %s: %s", + fu_plugin_get_name (self), + fu_device_get_id (device)); + g_signal_emit (self, signals[SIGNAL_DEVICE_REGISTER], 0, device); } /** * fu_plugin_device_remove: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @device: A #FuDevice * * Asks the daemon to remove a device from the exported list. @@ -477,33 +461,20 @@ * Since: 0.8.0 **/ void -fu_plugin_device_remove (FuPlugin *plugin, FuDevice *device) +fu_plugin_device_remove (FuPlugin *self, FuDevice *device) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - FuPluginHelper *helper; - - g_return_if_fail (FU_IS_PLUGIN (plugin)); + g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (FU_IS_DEVICE (device)); - /* waiting for add */ - helper = g_hash_table_lookup (priv->devices_delay, device); - if (helper != NULL) { - g_debug ("ignoring remove from delayed addition"); - g_source_remove (helper->timeout_id); - g_hash_table_remove (priv->devices_delay, helper->device); - fu_plugin_helper_free (helper); - return; - } - g_debug ("emit removed from %s: %s", - fu_plugin_get_name (plugin), + fu_plugin_get_name (self), fu_device_get_id (device)); - g_signal_emit (plugin, signals[SIGNAL_DEVICE_REMOVED], 0, device); + g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device); } /** * fu_plugin_request_recoldplug: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * * Ask all the plugins to coldplug all devices, which will include the prepare() * and cleanup() phases. Duplicate devices added will be ignored. @@ -511,15 +482,15 @@ * Since: 0.8.0 **/ void -fu_plugin_request_recoldplug (FuPlugin *plugin) +fu_plugin_request_recoldplug (FuPlugin *self) { - g_return_if_fail (FU_IS_PLUGIN (plugin)); - g_signal_emit (plugin, signals[SIGNAL_RECOLDPLUG], 0); + g_return_if_fail (FU_IS_PLUGIN (self)); + g_signal_emit (self, signals[SIGNAL_RECOLDPLUG], 0); } /** * fu_plugin_check_hwid: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @hwid: A Hardware ID GUID, e.g. `6de5d951-d755-576b-bd09-c5cf66b27234` * * Checks to see if a specific GUID exists. All hardware IDs on a @@ -530,17 +501,37 @@ * Since: 0.9.1 **/ gboolean -fu_plugin_check_hwid (FuPlugin *plugin, const gchar *hwid) +fu_plugin_check_hwid (FuPlugin *self, const gchar *hwid) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); if (priv->hwids == NULL) return FALSE; return fu_hwids_has_guid (priv->hwids, hwid); } /** + * fu_plugin_get_hwids: + * @self: A #FuPlugin + * + * Returns all the HWIDs defined in the system. All hardware IDs on a + * specific system can be shown using the `fwupdmgr hwids` command. + * + * Returns: (transfer none) (element-type utf-8): An array of GUIDs + * + * Since: 1.1.1 + **/ +GPtrArray * +fu_plugin_get_hwids (FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + if (priv->hwids == NULL) + return NULL; + return fu_hwids_get_guids (priv->hwids); +} + +/** * fu_plugin_check_supported: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @guid: A Hardware ID GUID, e.g. `6de5d951-d755-576b-bd09-c5cf66b27234` * * Checks to see if a specific device GUID is supported, i.e. available in the @@ -551,22 +542,16 @@ * Since: 1.0.0 **/ gboolean -fu_plugin_check_supported (FuPlugin *plugin, const gchar *guid) +fu_plugin_check_supported (FuPlugin *self, const gchar *guid) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - 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; } /** * fu_plugin_get_dmi_value: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @dmi_id: A DMI ID, e.g. `BiosVersion` * * Gets a hardware DMI value. @@ -576,9 +561,9 @@ * Since: 0.9.7 **/ const gchar * -fu_plugin_get_dmi_value (FuPlugin *plugin, const gchar *dmi_id) +fu_plugin_get_dmi_value (FuPlugin *self, const gchar *dmi_id) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); if (priv->hwids == NULL) return NULL; return fu_hwids_get_value (priv->hwids, dmi_id); @@ -586,7 +571,7 @@ /** * fu_plugin_get_smbios_string: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @structure_type: A SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS * @offset: A SMBIOS offset * @@ -600,9 +585,9 @@ * Since: 0.9.8 **/ const gchar * -fu_plugin_get_smbios_string (FuPlugin *plugin, guint8 structure_type, guint8 offset) +fu_plugin_get_smbios_string (FuPlugin *self, guint8 structure_type, guint8 offset) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); if (priv->smbios == NULL) return NULL; return fu_smbios_get_string (priv->smbios, structure_type, offset, NULL); @@ -610,7 +595,7 @@ /** * fu_plugin_get_smbios_data: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @structure_type: A SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS * * Gets a hardware SMBIOS data. @@ -620,40 +605,40 @@ * Since: 0.9.8 **/ GBytes * -fu_plugin_get_smbios_data (FuPlugin *plugin, guint8 structure_type) +fu_plugin_get_smbios_data (FuPlugin *self, guint8 structure_type) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); if (priv->smbios == NULL) return NULL; return fu_smbios_get_data (priv->smbios, structure_type, NULL); } void -fu_plugin_set_hwids (FuPlugin *plugin, FuHwids *hwids) +fu_plugin_set_hwids (FuPlugin *self, FuHwids *hwids) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); g_set_object (&priv->hwids, hwids); } void -fu_plugin_set_supported (FuPlugin *plugin, GPtrArray *supported_guids) +fu_plugin_set_udev_subsystems (FuPlugin *self, GPtrArray *udev_subsystems) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - if (priv->supported_guids != NULL) - g_ptr_array_unref (priv->supported_guids); - priv->supported_guids = g_ptr_array_ref (supported_guids); + FuPluginPrivate *priv = GET_PRIVATE (self); + if (priv->udev_subsystems != NULL) + g_ptr_array_unref (priv->udev_subsystems); + priv->udev_subsystems = g_ptr_array_ref (udev_subsystems); } void -fu_plugin_set_quirks (FuPlugin *plugin, FuQuirks *quirks) +fu_plugin_set_quirks (FuPlugin *self, FuQuirks *quirks) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); g_set_object (&priv->quirks, quirks); } /** * fu_plugin_get_quirks: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * * Returns the hardware database object. This can be used to discover device * quirks or other device-specific settings. @@ -663,35 +648,35 @@ * Since: 1.0.1 **/ FuQuirks * -fu_plugin_get_quirks (FuPlugin *plugin) +fu_plugin_get_quirks (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); return priv->quirks; } void -fu_plugin_set_runtime_versions (FuPlugin *plugin, GHashTable *runtime_versions) +fu_plugin_set_runtime_versions (FuPlugin *self, GHashTable *runtime_versions) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); priv->runtime_versions = g_hash_table_ref (runtime_versions); } /** * fu_plugin_add_runtime_version: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @component_id: An AppStream component id, e.g. "org.gnome.Software" * @version: A version string, e.g. "1.2.3" * - * Sets a runtime version of a specific dependancy. + * Sets a runtime version of a specific dependency. * * Since: 1.0.7 **/ void -fu_plugin_add_runtime_version (FuPlugin *plugin, +fu_plugin_add_runtime_version (FuPlugin *self, const gchar *component_id, const gchar *version) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); if (priv->runtime_versions == NULL) return; g_hash_table_insert (priv->runtime_versions, @@ -700,28 +685,28 @@ } void -fu_plugin_set_compile_versions (FuPlugin *plugin, GHashTable *compile_versions) +fu_plugin_set_compile_versions (FuPlugin *self, GHashTable *compile_versions) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); priv->compile_versions = g_hash_table_ref (compile_versions); } /** * fu_plugin_add_compile_version: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @component_id: An AppStream component id, e.g. "org.gnome.Software" * @version: A version string, e.g. "1.2.3" * - * Sets a compile-time version of a specific dependancy. + * Sets a compile-time version of a specific dependency. * * Since: 1.0.7 **/ void -fu_plugin_add_compile_version (FuPlugin *plugin, +fu_plugin_add_compile_version (FuPlugin *self, const gchar *component_id, const gchar *version) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); if (priv->compile_versions == NULL) return; g_hash_table_insert (priv->compile_versions, @@ -731,9 +716,9 @@ /** * fu_plugin_lookup_quirk_by_id: - * @plugin: A #FuPlugin - * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks" - * @id: An ID to match the entry, e.g. "012345" + * @self: A #FuPlugin + * @group: A string, e.g. "DfuFlags" + * @key: An ID to match the entry, e.g. "Summary" * * Looks up an entry in the hardware database using a string value. * @@ -742,67 +727,45 @@ * Since: 1.0.1 **/ const gchar * -fu_plugin_lookup_quirk_by_id (FuPlugin *plugin, const gchar *prefix, const gchar *id) +fu_plugin_lookup_quirk_by_id (FuPlugin *self, const gchar *group, const gchar *key) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL); - - /* wildcard */ - if (g_strstr_len (id, -1, "*") != NULL) - return fu_quirks_lookup_by_glob (priv->quirks, prefix, id); + FuPluginPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); /* exact ID */ - return fu_quirks_lookup_by_id (priv->quirks, prefix, id); -} - -/** - * fu_plugin_lookup_quirk_by_usb_device: - * @plugin: A #FuPlugin - * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks" - * @dev: A #GUsbDevice - * - * Looks up an entry in the hardware database using various keys generated - * from @dev. - * - * Returns: (transfer none): values from the database, or %NULL if not found - * - * Since: 1.0.1 - **/ -const gchar * -fu_plugin_lookup_quirk_by_usb_device (FuPlugin *plugin, const gchar *prefix, GUsbDevice *dev) -{ - FuPluginPrivate *priv = GET_PRIVATE (plugin); - g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL); - return fu_quirks_lookup_by_usb_device (priv->quirks, prefix, dev); + return fu_quirks_lookup_by_id (priv->quirks, group, key); } /** - * fu_plugin_get_supported: - * @plugin: A #FuPlugin + * fu_plugin_lookup_quirk_by_id_as_uint64: + * @self: A #FuPlugin + * @group: A string, e.g. "DfuFlags" + * @key: An ID to match the entry, e.g. "Size" * - * Gets all the device GUIDs supported by the daemon. + * Looks up an entry in the hardware database using a string key, returning + * an integer value. Values are assumed base 10, unless prefixed with "0x" + * where they are parsed as base 16. * - * Returns: (element-type utf8) (transfer none): GUIDs + * Returns: (transfer none): value from the database, or 0 if not found * - * Since: 1.0.0 + * Since: 1.1.2 **/ -GPtrArray * -fu_plugin_get_supported (FuPlugin *plugin) +guint64 +fu_plugin_lookup_quirk_by_id_as_uint64 (FuPlugin *self, const gchar *group, const gchar *key) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); - return priv->supported_guids; + return fu_common_strtoull (fu_plugin_lookup_quirk_by_id (self, group, key)); } void -fu_plugin_set_smbios (FuPlugin *plugin, FuSmbios *smbios) +fu_plugin_set_smbios (FuPlugin *self, FuSmbios *smbios) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); g_set_object (&priv->smbios, smbios); } /** * fu_plugin_set_coldplug_delay: - * @plugin: A #FuPlugin + * @self: A #FuPlugin * @duration: A delay in milliseconds * * Set the minimum time that should be waited inbetween the call to @@ -820,9 +783,9 @@ * Since: 0.8.0 **/ void -fu_plugin_set_coldplug_delay (FuPlugin *plugin, guint duration) +fu_plugin_set_coldplug_delay (FuPlugin *self, guint duration) { - g_return_if_fail (FU_IS_PLUGIN (plugin)); + g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (duration > 0); /* check sanity */ @@ -834,14 +797,15 @@ } /* emit */ - g_signal_emit (plugin, signals[SIGNAL_SET_COLDPLUG_DELAY], 0, duration); + g_signal_emit (self, signals[SIGNAL_SET_COLDPLUG_DELAY], 0, duration); } gboolean -fu_plugin_runner_startup (FuPlugin *plugin, GError **error) +fu_plugin_runner_startup (FuPlugin *self, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -856,8 +820,18 @@ if (func == NULL) return TRUE; g_debug ("performing startup() on %s", priv->name); - if (!func (plugin, 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; @@ -890,11 +864,21 @@ fu_plugin_runner_offline_setup (GError **error) { gint rc; + g_autofree gchar *filename = NULL; + g_autofree gchar *symlink_target = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + /* does already exist */ + filename = fu_common_realpath (FU_OFFLINE_TRIGGER_FILENAME, NULL); + if (g_strcmp0 (filename, symlink_target) == 0) { + g_debug ("%s already points to %s, skipping creation", + FU_OFFLINE_TRIGGER_FILENAME, symlink_target); + return TRUE; + } + /* create symlink for the systemd-system-update-generator */ - rc = symlink ("/var/lib/fwupd", FU_OFFLINE_TRIGGER_FILENAME); + rc = symlink (symlink_target, FU_OFFLINE_TRIGGER_FILENAME); if (rc < 0) { g_set_error (error, FWUPD_ERROR, @@ -908,11 +892,51 @@ } static gboolean -fu_plugin_runner_device_generic (FuPlugin *plugin, FuDevice *device, +fu_plugin_runner_device_generic (FuPlugin *self, FuDevice *device, const gchar *symbol_name, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; + + /* not enabled */ + if (!priv->enabled) + return TRUE; + + /* no object loaded */ + if (priv->module == NULL) + return TRUE; + + /* optional */ + g_module_symbol (priv->module, symbol_name, (gpointer *) &func); + if (func == NULL) + return TRUE; + g_debug ("performing %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; +} + +static gboolean +fu_plugin_runner_flagged_device_generic (FuPlugin *self, FwupdInstallFlags flags, + FuDevice *device, + const gchar *symbol_name, GError **error) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + FuPluginFlaggedDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -927,21 +951,31 @@ if (func == NULL) return TRUE; g_debug ("performing %s() on %s", symbol_name + 10, priv->name); - if (!func (plugin, 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; + } static gboolean -fu_plugin_runner_device_array_generic (FuPlugin *plugin, GPtrArray *devices, +fu_plugin_runner_device_array_generic (FuPlugin *self, GPtrArray *devices, const gchar *symbol_name, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceArrayFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -956,20 +990,29 @@ if (func == NULL) return TRUE; g_debug ("performing %s() on %s", symbol_name + 10, priv->name); - if (!func (plugin, 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; } gboolean -fu_plugin_runner_coldplug (FuPlugin *plugin, GError **error) +fu_plugin_runner_coldplug (FuPlugin *self, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -984,18 +1027,28 @@ if (func == NULL) return TRUE; g_debug ("performing coldplug() on %s", priv->name); - if (!func (plugin, 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; } gboolean -fu_plugin_runner_recoldplug (FuPlugin *plugin, GError **error) +fu_plugin_runner_recoldplug (FuPlugin *self, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1010,18 +1063,29 @@ if (func == NULL) return TRUE; g_debug ("performing recoldplug() on %s", priv->name); - if (!func (plugin, 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; } gboolean -fu_plugin_runner_coldplug_prepare (FuPlugin *plugin, GError **error) +fu_plugin_runner_coldplug_prepare (FuPlugin *self, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1036,18 +1100,29 @@ if (func == NULL) return TRUE; g_debug ("performing coldplug_prepare() on %s", priv->name); - if (!func (plugin, 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; } gboolean -fu_plugin_runner_coldplug_cleanup (FuPlugin *plugin, GError **error) +fu_plugin_runner_coldplug_cleanup (FuPlugin *self, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginStartupFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1062,69 +1137,106 @@ if (func == NULL) return TRUE; g_debug ("performing coldplug_cleanup() on %s", priv->name); - if (!func (plugin, 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; } gboolean -fu_plugin_runner_composite_prepare (FuPlugin *plugin, GPtrArray *devices, GError **error) +fu_plugin_runner_composite_prepare (FuPlugin *self, GPtrArray *devices, GError **error) { - return fu_plugin_runner_device_array_generic (plugin, devices, + return fu_plugin_runner_device_array_generic (self, devices, "fu_plugin_composite_prepare", error); } gboolean -fu_plugin_runner_composite_cleanup (FuPlugin *plugin, GPtrArray *devices, GError **error) +fu_plugin_runner_composite_cleanup (FuPlugin *self, GPtrArray *devices, GError **error) { - return fu_plugin_runner_device_array_generic (plugin, devices, + return fu_plugin_runner_device_array_generic (self, devices, "fu_plugin_composite_cleanup", error); } gboolean -fu_plugin_runner_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_update_prepare (FuPlugin *self, FwupdInstallFlags flags, FuDevice *device, + GError **error) { - return fu_plugin_runner_device_generic (plugin, device, - "fu_plugin_update_prepare", error); + return fu_plugin_runner_flagged_device_generic (self, flags, device, + "fu_plugin_update_prepare", + error); } gboolean -fu_plugin_runner_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_update_cleanup (FuPlugin *self, FwupdInstallFlags flags, FuDevice *device, + GError **error) { - return fu_plugin_runner_device_generic (plugin, device, - "fu_plugin_update_cleanup", error); + return fu_plugin_runner_flagged_device_generic (self, flags, device, + "fu_plugin_update_cleanup", + error); } gboolean -fu_plugin_runner_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_update_attach (FuPlugin *self, FuDevice *device, GError **error) { - return fu_plugin_runner_device_generic (plugin, device, + return fu_plugin_runner_device_generic (self, device, "fu_plugin_update_attach", error); } gboolean -fu_plugin_runner_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_update_detach (FuPlugin *self, FuDevice *device, GError **error) { - return fu_plugin_runner_device_generic (plugin, device, + return fu_plugin_runner_device_generic (self, device, "fu_plugin_update_detach", error); } gboolean -fu_plugin_runner_update_reload (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_update_reload (FuPlugin *self, FuDevice *device, GError **error) { - return fu_plugin_runner_device_generic (plugin, device, + return fu_plugin_runner_device_generic (self, device, "fu_plugin_update_reload", error); } +/** + * fu_plugin_add_udev_subsystem: + * @self: a #FuPlugin + * @subsystem: a subsystem name, e.g. `pciport` + * + * Registers the udev subsystem to be watched by the daemon. + * + * Plugins can use this method only in fu_plugin_init() + **/ +void +fu_plugin_add_udev_subsystem (FuPlugin *self, const gchar *subsystem) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + for (guint i = 0; i < priv->udev_subsystems->len; i++) { + const gchar *subsystem_tmp = g_ptr_array_index (priv->udev_subsystems, i); + if (g_strcmp0 (subsystem_tmp, subsystem) == 0) + return; + } + g_debug ("added udev subsystem watch of %s", subsystem); + g_ptr_array_add (priv->udev_subsystems, g_strdup (subsystem)); +} + gboolean -fu_plugin_runner_usb_device_added (FuPlugin *plugin, GUsbDevice *usb_device, GError **error) +fu_plugin_runner_usb_device_added (FuPlugin *self, FuUsbDevice *device, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginUsbDeviceAddedFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1136,17 +1248,78 @@ /* 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 (plugin, usb_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; +} + +gboolean +fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError **error) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + FuPluginUdevDeviceAddedFunc func = NULL; + g_autoptr(GError) error_local = NULL; + + /* not enabled */ + if (!priv->enabled) + return TRUE; + + /* no object loaded */ + if (priv->module == NULL) + return TRUE; + + /* optional */ + g_module_symbol (priv->module, "fu_plugin_udev_device_added", (gpointer *) &func); + 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; } void -fu_plugin_runner_device_register (FuPlugin *plugin, FuDevice *device) +fu_plugin_runner_device_removed (FuPlugin *self, FuDevice *device) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + g_autoptr(GError) error_local= NULL; + + if (!fu_plugin_runner_device_generic (self, device, + "fu_plugin_device_removed", + &error_local)) + g_warning ("%s", error_local->message); +} + +void +fu_plugin_runner_device_register (FuPlugin *self, FuDevice *device) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceRegisterFunc func = NULL; /* not enabled */ @@ -1156,42 +1329,45 @@ return; /* don't notify plugins on their own devices */ - if (g_strcmp0 (fu_device_get_plugin (device), fu_plugin_get_name (plugin)) == 0) + if (g_strcmp0 (fu_device_get_plugin (device), fu_plugin_get_name (self)) == 0) return; /* optional */ g_module_symbol (priv->module, "fu_plugin_device_registered", (gpointer *) &func); if (func != NULL) { - g_debug ("performing device_added() on %s", priv->name); - func (plugin, device); + g_debug ("performing fu_plugin_device_registered() on %s", priv->name); + func (self, device); } } -static gboolean -fu_plugin_runner_schedule_update (FuPlugin *plugin, +gboolean +fu_plugin_runner_schedule_update (FuPlugin *self, FuDevice *device, + FwupdRelease *release, GBytes *blob_cab, + FwupdInstallFlags flags, GError **error) { - FwupdRelease *release; - gchar tmpname[] = {"XXXXXX.cap"}; + gchar tmpname[] = {"XXXXXX.cab"}; g_autofree gchar *dirname = NULL; g_autofree gchar *filename = NULL; - g_autoptr(FuDevice) res_tmp = NULL; g_autoptr(FuHistory) history = NULL; - g_autoptr(FwupdRelease) release_tmp = fwupd_release_new (); g_autoptr(GFile) file = NULL; /* id already exists */ history = fu_history_new (); - res_tmp = fu_history_get_device_by_id (history, fu_device_get_id (device), NULL); - if (res_tmp != NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_ALREADY_PENDING, - "%s is already scheduled to be updated", - fu_device_get_id (device)); - return FALSE; + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_autoptr(FuDevice) res_tmp = NULL; + res_tmp = fu_history_get_device_by_id (history, fu_device_get_id (device), NULL); + if (res_tmp != NULL && + fu_device_get_update_state (res_tmp) == FWUPD_UPDATE_STATE_PENDING) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_ALREADY_PENDING, + "%s is already scheduled to be updated", + fu_device_get_id (device)); + return FALSE; + } } /* create directory */ @@ -1218,28 +1394,29 @@ /* schedule for next boot */ g_debug ("schedule %s to be installed to %s on next boot", filename, fu_device_get_id (device)); - release = fu_device_get_release_default (device); - fwupd_release_set_version (release_tmp, fwupd_release_get_version (release)); - fwupd_release_set_filename (release_tmp, filename); + fwupd_release_set_filename (release, filename); /* add to database */ + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); fu_device_set_update_state (device, FWUPD_UPDATE_STATE_PENDING); - if (!fu_history_add_device (history, device, release_tmp, error)) + if (!fu_history_add_device (history, device, release, error)) return FALSE; /* next boot we run offline */ + fu_device_set_progress (device, 100); return fu_plugin_runner_offline_setup (error); } gboolean -fu_plugin_runner_verify (FuPlugin *plugin, +fu_plugin_runner_verify (FuPlugin *self, FuDevice *device, FuPluginVerifyFlags flags, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginVerifyFunc func = NULL; GPtrArray *checksums; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1249,24 +1426,85 @@ 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 additional detach */ + if (!fu_plugin_runner_device_generic (self, device, + "fu_plugin_verify_detach", + error)) + return FALSE; + + /* run vfunc */ g_debug ("performing verify() on %s", priv->name); - if (!func (plugin, device, flags, error)) { - g_prefix_error (error, "failed to verify %s: ", priv->name); + if (!func (self, device, flags, &error_local)) { + g_autoptr(GError) error_attach = NULL; + 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); + /* make the device "work" again, but don't prefix the error */ + if (!fu_plugin_runner_device_generic (self, device, + "fu_plugin_verify_attach", + &error_attach)) { + g_warning ("failed to attach whilst aborting verify(): %s", + error_attach->message); + } return FALSE; } + + /* run optional attach */ + if (!fu_plugin_runner_device_generic (self, device, + "fu_plugin_verify_attach", + error)) + return FALSE; + + /* success */ return TRUE; } gboolean -fu_plugin_runner_unlock (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_activate (FuPlugin *self, FuDevice *device, GError **error) +{ + guint64 flags; + + /* final check */ + flags = fu_device_get_flags (device); + if ((flags & FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s does not need activation", + fu_device_get_id (device)); + return FALSE; + } + + /* run vfunc */ + if (!fu_plugin_runner_device_generic (self, device, + "fu_plugin_activate", error)) + return FALSE; + + /* update with correct flags */ + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC); + return TRUE; +} + +gboolean +fu_plugin_runner_unlock (FuPlugin *self, FuDevice *device, GError **error) { guint64 flags; @@ -1282,7 +1520,7 @@ } /* run vfunc */ - if (!fu_plugin_runner_device_generic (plugin, device, + if (!fu_plugin_runner_device_generic (self, device, "fu_plugin_unlock", error)) return FALSE; @@ -1294,19 +1532,17 @@ } gboolean -fu_plugin_runner_update (FuPlugin *plugin, +fu_plugin_runner_update (FuPlugin *self, FuDevice *device, - GBytes *blob_cab, GBytes *blob_fw, FwupdInstallFlags flags, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (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) { @@ -1330,21 +1566,6 @@ return FALSE; } - /* just schedule this for the next reboot */ - if (flags & FWUPD_INSTALL_FLAG_OFFLINE) { - if (blob_cab == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "No cabinet archive to schedule"); - return FALSE; - } - return fu_plugin_runner_schedule_update (plugin, - device, - blob_cab, - error); - } - /* cancel the pending action */ if (!fu_plugin_runner_offline_invalidate (error)) return FALSE; @@ -1352,15 +1573,27 @@ /* online */ history = fu_history_new (); device_pending = fu_history_get_device_by_id (history, fu_device_get_id (device), NULL); - if (!update_func (plugin, device, blob_fw, flags, &error_update)) { - fu_device_set_update_error (device, error_update->message); - g_propagate_error (error, 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_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) { @@ -1378,15 +1611,15 @@ 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; } } @@ -1395,10 +1628,11 @@ } gboolean -fu_plugin_runner_clear_results (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_clear_results (FuPlugin *self, FuDevice *device, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1413,18 +1647,29 @@ if (func == NULL) return TRUE; g_debug ("performing clear_result() on %s", priv->name); - if (!func (plugin, 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; } gboolean -fu_plugin_runner_get_results (FuPlugin *plugin, FuDevice *device, GError **error) +fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginDeviceFunc func = NULL; + g_autoptr(GError) error_local = NULL; /* not enabled */ if (!priv->enabled) @@ -1439,8 +1684,18 @@ if (func == NULL) return TRUE; g_debug ("performing get_results() on %s", priv->name); - if (!func (plugin, 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; @@ -1448,7 +1703,7 @@ /** * fu_plugin_get_order: - * @plugin: a #FuPlugin + * @self: a #FuPlugin * * Gets the plugin order, where higher numbers are run after lower * numbers. @@ -1456,30 +1711,59 @@ * Returns: the integer value **/ guint -fu_plugin_get_order (FuPlugin *plugin) +fu_plugin_get_order (FuPlugin *self) { - FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); return priv->order; } /** * fu_plugin_set_order: - * @plugin: a #FuPlugin + * @self: a #FuPlugin * @order: a integer value * * Sets the plugin order, where higher numbers are run after lower * numbers. **/ void -fu_plugin_set_order (FuPlugin *plugin, guint order) +fu_plugin_set_order (FuPlugin *self, guint order) { - FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); priv->order = order; } /** + * fu_plugin_get_priority: + * @self: a #FuPlugin + * + * Gets the plugin priority, where higher numbers are better. + * + * Returns: the integer value + **/ +guint +fu_plugin_get_priority (FuPlugin *self) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); + return priv->priority; +} + +/** + * fu_plugin_set_priority: + * @self: a #FuPlugin + * @priority: a integer value + * + * Sets the plugin priority, where higher numbers are better. + **/ +void +fu_plugin_set_priority (FuPlugin *self, guint priority) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); + priv->priority = priority; +} + +/** * fu_plugin_add_rule: - * @plugin: a #FuPlugin + * @self: a #FuPlugin * @rule: a #FuPluginRule, e.g. %FU_PLUGIN_RULE_CONFLICTS * @name: a plugin name, e.g. `upower` * @@ -1491,15 +1775,16 @@ * If depsolving fails then fwupd will not start. **/ void -fu_plugin_add_rule (FuPlugin *plugin, FuPluginRule rule, const gchar *name) +fu_plugin_add_rule (FuPlugin *self, FuPluginRule rule, const gchar *name) { - FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + 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); } /** * fu_plugin_get_rules: - * @plugin: a #FuPlugin + * @self: a #FuPlugin * @rule: a #FuPluginRule, e.g. %FU_PLUGIN_RULE_CONFLICTS * * Gets the plugin IDs that should be run after this plugin. @@ -1507,15 +1792,37 @@ * Returns: (element-type utf8) (transfer none): the list of plugin names, e.g. ['appstream'] **/ GPtrArray * -fu_plugin_get_rules (FuPlugin *plugin, FuPluginRule rule) +fu_plugin_get_rules (FuPlugin *self, FuPluginRule rule) { - FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); return priv->rules[rule]; } /** + * fu_plugin_has_rule: + * @self: a #FuPlugin + * @rule: a #FuPluginRule, e.g. %FU_PLUGIN_RULE_CONFLICTS + * @name: a plugin name, e.g. `upower` + * + * Gets the plugin IDs that should be run after this plugin. + * + * Returns: %TRUE if the name exists for the specific rule + **/ +gboolean +fu_plugin_has_rule (FuPlugin *self, FuPluginRule rule, const gchar *name) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); + for (guint i = 0; i < priv->rules[rule]->len; i++) { + const gchar *tmp = g_ptr_array_index (priv->rules[rule], i); + if (g_strcmp0 (tmp, name) == 0) + return TRUE; + } + return FALSE; +} + +/** * fu_plugin_add_report_metadata: - * @plugin: a #FuPlugin + * @self: a #FuPlugin * @key: a string, e.g. `FwupdateVersion` * @value: a string, e.g. `10` * @@ -1526,30 +1833,30 @@ * confirmation. **/ void -fu_plugin_add_report_metadata (FuPlugin *plugin, const gchar *key, const gchar *value) +fu_plugin_add_report_metadata (FuPlugin *self, const gchar *key, const gchar *value) { - FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); g_hash_table_insert (priv->report_metadata, g_strdup (key), g_strdup (value)); } /** * fu_plugin_get_report_metadata: - * @plugin: a #FuPlugin + * @self: a #FuPlugin * * Returns the list of additional metadata to be added when filing a report. * * Returns: (transfer none): the map of report metadata **/ GHashTable * -fu_plugin_get_report_metadata (FuPlugin *plugin) +fu_plugin_get_report_metadata (FuPlugin *self) { - FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + FuPluginPrivate *priv = fu_plugin_get_instance_private (self); return priv->report_metadata; } /** * fu_plugin_get_config_value: - * @plugin: a #FuPlugin + * @self: a #FuPlugin * @key: A settings key * * Return the value of a key if it's been configured @@ -1557,7 +1864,7 @@ * Since: 1.0.6 **/ gchar * -fu_plugin_get_config_value (FuPlugin *plugin, const gchar *key) +fu_plugin_get_config_value (FuPlugin *self, const gchar *key) { g_autofree gchar *conf_dir = NULL; g_autofree gchar *conf_file = NULL; @@ -1566,7 +1873,7 @@ const gchar *plugin_name; conf_dir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR_PKG); - plugin_name = fu_plugin_get_name (plugin); + plugin_name = fu_plugin_get_name (self); conf_file = g_strdup_printf ("%s.conf", plugin_name); conf_path = g_build_filename (conf_dir, conf_file, NULL); if (!g_file_test (conf_path, G_FILE_TEST_IS_REGULAR)) @@ -1651,16 +1958,28 @@ 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 -fu_plugin_init (FuPlugin *plugin) +fu_plugin_init (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPluginPrivate *priv = GET_PRIVATE (self); priv->enabled = TRUE; priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); - priv->devices_delay = g_hash_table_new (g_direct_hash, g_direct_equal); + g_rw_lock_init (&priv->devices_mutex); priv->report_metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); for (guint i = 0; i < FU_PLUGIN_RULE_LAST; i++) priv->rules[i] = g_ptr_array_new_with_free_func (g_free); @@ -1669,8 +1988,8 @@ static void fu_plugin_finalize (GObject *object) { - FuPlugin *plugin = FU_PLUGIN (object); - FuPluginPrivate *priv = GET_PRIVATE (plugin); + FuPlugin *self = FU_PLUGIN (object); + FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginInitFunc func = NULL; /* optional */ @@ -1678,7 +1997,7 @@ g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func); if (func != NULL) { g_debug ("performing destroy() on %s", priv->name); - func (plugin); + func (self); } } @@ -1691,8 +2010,8 @@ 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) g_object_unref (priv->smbios); if (priv->runtime_versions != NULL) @@ -1700,8 +2019,9 @@ if (priv->compile_versions != NULL) g_hash_table_unref (priv->compile_versions); g_hash_table_unref (priv->devices); - g_hash_table_unref (priv->devices_delay); g_hash_table_unref (priv->report_metadata); + g_rw_lock_clear (&priv->devices_mutex); + g_free (priv->build_hash); g_free (priv->name); g_free (priv->data); /* Must happen as the last step to avoid prematurely @@ -1717,7 +2037,5 @@ FuPlugin * fu_plugin_new (void) { - FuPlugin *plugin; - plugin = g_object_new (FU_TYPE_PLUGIN, NULL); - return plugin; + return FU_PLUGIN (g_object_new (FU_TYPE_PLUGIN, NULL)); } diff -Nru fwupd-1.0.9/src/fu-plugin.h fwupd-1.2.10/src/fu-plugin.h --- fwupd-1.0.9/src/fu-plugin.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-plugin.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,24 +1,25 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_PLUGIN_H -#define __FU_PLUGIN_H +#pragma once -#include #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" #include "fu-hwids.h" #include "fu-usb-device.h" +#include "fu-udev-device.h" +#include "fwupd-common.h" G_BEGIN_DECLS @@ -29,21 +30,24 @@ { GObjectClass parent_class; /* signals */ - void (* device_added) (FuPlugin *plugin, + void (* device_added) (FuPlugin *self, FuDevice *device); - void (* device_removed) (FuPlugin *plugin, + void (* device_removed) (FuPlugin *self, FuDevice *device); - void (* status_changed) (FuPlugin *plugin, + void (* status_changed) (FuPlugin *self, FwupdStatus status); - void (* percentage_changed) (FuPlugin *plugin, + void (* percentage_changed) (FuPlugin *self, guint percentage); - void (* recoldplug) (FuPlugin *plugin); - void (* set_coldplug_delay) (FuPlugin *plugin, + void (* recoldplug) (FuPlugin *self); + void (* set_coldplug_delay) (FuPlugin *self, guint duration); - void (* device_register) (FuPlugin *plugin, + 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]; }; /** @@ -63,6 +67,10 @@ * @FU_PLUGIN_RULE_CONFLICTS: The plugin conflicts with another * @FU_PLUGIN_RULE_RUN_AFTER: Order the plugin after another * @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(). @@ -71,6 +79,10 @@ FU_PLUGIN_RULE_CONFLICTS, FU_PLUGIN_RULE_RUN_AFTER, 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; @@ -78,71 +90,66 @@ typedef struct FuPluginData FuPluginData; /* for plugins to use */ -const gchar *fu_plugin_get_name (FuPlugin *plugin); -FuPluginData *fu_plugin_get_data (FuPlugin *plugin); -FuPluginData *fu_plugin_alloc_data (FuPlugin *plugin, +const gchar *fu_plugin_get_name (FuPlugin *self); +FuPluginData *fu_plugin_get_data (FuPlugin *self); +FuPluginData *fu_plugin_alloc_data (FuPlugin *self, gsize data_sz); -gboolean fu_plugin_get_enabled (FuPlugin *plugin); -void fu_plugin_set_enabled (FuPlugin *plugin, +gboolean fu_plugin_get_enabled (FuPlugin *self); +void fu_plugin_set_enabled (FuPlugin *self, gboolean enabled); -GUsbContext *fu_plugin_get_usb_context (FuPlugin *plugin); -GPtrArray *fu_plugin_get_supported (FuPlugin *plugin); -void fu_plugin_device_add (FuPlugin *plugin, - FuDevice *device); -void fu_plugin_device_add_delay (FuPlugin *plugin, +void fu_plugin_set_build_hash (FuPlugin *self, + const gchar *build_hash); +GUsbContext *fu_plugin_get_usb_context (FuPlugin *self); +void fu_plugin_device_add (FuPlugin *self, FuDevice *device); -void fu_plugin_device_remove (FuPlugin *plugin, +void fu_plugin_device_remove (FuPlugin *self, FuDevice *device); -void fu_plugin_device_register (FuPlugin *plugin, +void fu_plugin_device_register (FuPlugin *self, FuDevice *device); -void fu_plugin_set_status (FuPlugin *plugin, - FwupdStatus status); -void fu_plugin_set_percentage (FuPlugin *plugin, - guint percentage); -void fu_plugin_request_recoldplug (FuPlugin *plugin); -void fu_plugin_set_coldplug_delay (FuPlugin *plugin, +void fu_plugin_request_recoldplug (FuPlugin *self); +void fu_plugin_set_coldplug_delay (FuPlugin *self, guint duration); -gpointer fu_plugin_cache_lookup (FuPlugin *plugin, +gpointer fu_plugin_cache_lookup (FuPlugin *self, const gchar *id); -void fu_plugin_cache_remove (FuPlugin *plugin, +void fu_plugin_cache_remove (FuPlugin *self, const gchar *id); -void fu_plugin_cache_add (FuPlugin *plugin, +void fu_plugin_cache_add (FuPlugin *self, const gchar *id, gpointer dev); -gboolean fu_plugin_check_hwid (FuPlugin *plugin, +gboolean fu_plugin_check_hwid (FuPlugin *self, const gchar *hwid); -gboolean fu_plugin_check_supported (FuPlugin *plugin, +GPtrArray *fu_plugin_get_hwids (FuPlugin *self); +gboolean fu_plugin_check_supported (FuPlugin *self, const gchar *guid); -const gchar *fu_plugin_get_dmi_value (FuPlugin *plugin, +const gchar *fu_plugin_get_dmi_value (FuPlugin *self, const gchar *dmi_id); -const gchar *fu_plugin_get_smbios_string (FuPlugin *plugin, +const gchar *fu_plugin_get_smbios_string (FuPlugin *self, guint8 structure_type, guint8 offset); -GBytes *fu_plugin_get_smbios_data (FuPlugin *plugin, +GBytes *fu_plugin_get_smbios_data (FuPlugin *self, guint8 structure_type); -void fu_plugin_add_rule (FuPlugin *plugin, +void fu_plugin_add_rule (FuPlugin *self, FuPluginRule rule, const gchar *name); -FuQuirks *fu_plugin_get_quirks (FuPlugin *plugin); -const gchar *fu_plugin_lookup_quirk_by_id (FuPlugin *plugin, - const gchar *prefix, - const gchar *id); -const gchar *fu_plugin_lookup_quirk_by_usb_device (FuPlugin *plugin, - const gchar *prefix, - GUsbDevice *dev); -void fu_plugin_add_report_metadata (FuPlugin *plugin, +void fu_plugin_add_udev_subsystem (FuPlugin *self, + const gchar *subsystem); +FuQuirks *fu_plugin_get_quirks (FuPlugin *self); +const gchar *fu_plugin_lookup_quirk_by_id (FuPlugin *self, + const gchar *group, + const gchar *key); +guint64 fu_plugin_lookup_quirk_by_id_as_uint64 (FuPlugin *self, + const gchar *group, + const gchar *key); +void fu_plugin_add_report_metadata (FuPlugin *self, const gchar *key, const gchar *value); -gchar *fu_plugin_get_config_value (FuPlugin *plugin, +gchar *fu_plugin_get_config_value (FuPlugin *self, const gchar *key); -void fu_plugin_add_runtime_version (FuPlugin *plugin, +void fu_plugin_add_runtime_version (FuPlugin *self, const gchar *component_id, const gchar *version); -void fu_plugin_add_compile_version (FuPlugin *plugin, +void fu_plugin_add_compile_version (FuPlugin *self, const gchar *component_id, const gchar *version); G_END_DECLS - -#endif /* __FU_PLUGIN_H */ - diff -Nru fwupd-1.0.9/src/fu-plugin-list.c fwupd-1.2.10/src/fu-plugin-list.c --- fwupd-1.0.9/src/fu-plugin-list.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-plugin-list.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,10 +1,11 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuPluginList" + #include "config.h" #include @@ -20,7 +21,7 @@ * * This list of plugins provides a way to get the specific plugin quickly using * a hash table and also any plugin-list specific functionality such as - * sorting by dependancy order. + * sorting by dependency order. * * See also: #FuPlugin */ @@ -196,6 +197,36 @@ changes = TRUE; } } + } + + /* set priority as well */ + for (guint i = 0; i < self->plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_BETTER_THAN); + for (guint j = 0; j < deps->len && !changes; j++) { + const gchar *plugin_name = g_ptr_array_index (deps, j); + dep = fu_plugin_list_find_by_name (self, plugin_name, NULL); + if (dep == NULL) { + g_debug ("cannot find plugin '%s' " + "referenced by '%s'", + plugin_name, + fu_plugin_get_name (plugin)); + continue; + } + if (!fu_plugin_get_enabled (dep)) + continue; + if (fu_plugin_get_priority (plugin) <= fu_plugin_get_priority (dep)) { + g_debug ("%s [%u] better than %s [%u] " + "so bumping to [%u]", + fu_plugin_get_name (plugin), + fu_plugin_get_priority (plugin), + fu_plugin_get_name (dep), + fu_plugin_get_priority (dep), + fu_plugin_get_priority (dep) + 1); + fu_plugin_set_priority (plugin, fu_plugin_get_priority (dep) + 1); + changes = TRUE; + } + } } /* check we're not stuck */ diff -Nru fwupd-1.0.9/src/fu-plugin-list.h fwupd-1.2.10/src/fu-plugin-list.h --- fwupd-1.0.9/src/fu-plugin-list.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-plugin-list.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,19 +1,17 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_PLUGIN_LIST_H -#define __FU_PLUGIN_LIST_H - -G_BEGIN_DECLS +#pragma once #include #include "fu-plugin.h" +G_BEGIN_DECLS + #define FU_TYPE_PLUGIN_LIST (fu_plugin_list_get_type ()) G_DECLARE_FINAL_TYPE (FuPluginList, fu_plugin_list, FU, PLUGIN_LIST, GObject) @@ -28,6 +26,3 @@ GError **error); G_END_DECLS - -#endif /* __FU_PLUGIN_LIST_H */ - diff -Nru fwupd-1.0.9/src/fu-plugin-private.h fwupd-1.2.10/src/fu-plugin-private.h --- fwupd-1.0.9/src/fu-plugin-private.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-plugin-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_PLUGIN_PRIVATE_H -#define __FU_PLUGIN_PRIVATE_H +#pragma once #include "fu-quirks.h" #include "fu-plugin.h" @@ -17,87 +15,108 @@ #define FU_OFFLINE_TRIGGER_FILENAME FU_OFFLINE_DESTDIR "/system-update" FuPlugin *fu_plugin_new (void); -gboolean fu_plugin_has_device_delay (FuPlugin *plugin); -void fu_plugin_set_usb_context (FuPlugin *plugin, +void fu_plugin_set_usb_context (FuPlugin *self, GUsbContext *usb_ctx); -void fu_plugin_set_hwids (FuPlugin *plugin, +void fu_plugin_set_hwids (FuPlugin *self, FuHwids *hwids); -void fu_plugin_set_supported (FuPlugin *plugin, - GPtrArray *supported_guids); -void fu_plugin_set_quirks (FuPlugin *plugin, +void fu_plugin_set_udev_subsystems (FuPlugin *self, + GPtrArray *udev_subsystems); +void fu_plugin_set_quirks (FuPlugin *self, FuQuirks *quirks); -void fu_plugin_set_runtime_versions (FuPlugin *plugin, +void fu_plugin_set_runtime_versions (FuPlugin *self, GHashTable *runtime_versions); -void fu_plugin_set_compile_versions (FuPlugin *plugin, +void fu_plugin_set_compile_versions (FuPlugin *self, GHashTable *compile_versions); -void fu_plugin_set_smbios (FuPlugin *plugin, +void fu_plugin_set_smbios (FuPlugin *self, FuSmbios *smbios); -guint fu_plugin_get_order (FuPlugin *plugin); -void fu_plugin_set_order (FuPlugin *plugin, +guint fu_plugin_get_order (FuPlugin *self); +void fu_plugin_set_order (FuPlugin *self, guint order); -void fu_plugin_set_name (FuPlugin *plugin, +guint fu_plugin_get_priority (FuPlugin *self); +void fu_plugin_set_priority (FuPlugin *self, + guint priority); +void fu_plugin_set_name (FuPlugin *self, const gchar *name); -GPtrArray *fu_plugin_get_rules (FuPlugin *plugin, +const gchar *fu_plugin_get_build_hash (FuPlugin *self); +GPtrArray *fu_plugin_get_rules (FuPlugin *self, FuPluginRule rule); -GHashTable *fu_plugin_get_report_metadata (FuPlugin *plugin); -gboolean fu_plugin_open (FuPlugin *plugin, +gboolean fu_plugin_has_rule (FuPlugin *self, + FuPluginRule rule, + const gchar *name); +GHashTable *fu_plugin_get_report_metadata (FuPlugin *self); +gboolean fu_plugin_open (FuPlugin *self, const gchar *filename, GError **error); -gboolean fu_plugin_runner_startup (FuPlugin *plugin, +gboolean fu_plugin_runner_startup (FuPlugin *self, GError **error); -gboolean fu_plugin_runner_coldplug (FuPlugin *plugin, +gboolean fu_plugin_runner_coldplug (FuPlugin *self, GError **error); -gboolean fu_plugin_runner_coldplug_prepare (FuPlugin *plugin, +gboolean fu_plugin_runner_coldplug_prepare (FuPlugin *self, GError **error); -gboolean fu_plugin_runner_coldplug_cleanup (FuPlugin *plugin, +gboolean fu_plugin_runner_coldplug_cleanup (FuPlugin *self, GError **error); -gboolean fu_plugin_runner_recoldplug (FuPlugin *plugin, +gboolean fu_plugin_runner_recoldplug (FuPlugin *self, GError **error); -gboolean fu_plugin_runner_update_prepare (FuPlugin *plugin, +gboolean fu_plugin_runner_update_prepare (FuPlugin *self, + FwupdInstallFlags flags, FuDevice *device, GError **error); -gboolean fu_plugin_runner_update_cleanup (FuPlugin *plugin, +gboolean fu_plugin_runner_update_cleanup (FuPlugin *self, + FwupdInstallFlags flags, FuDevice *device, GError **error); -gboolean fu_plugin_runner_composite_prepare (FuPlugin *plugin, +gboolean fu_plugin_runner_composite_prepare (FuPlugin *self, GPtrArray *devices, GError **error); -gboolean fu_plugin_runner_composite_cleanup (FuPlugin *plugin, +gboolean fu_plugin_runner_composite_cleanup (FuPlugin *self, GPtrArray *devices, GError **error); -gboolean fu_plugin_runner_update_attach (FuPlugin *plugin, +gboolean fu_plugin_runner_update_attach (FuPlugin *self, FuDevice *device, GError **error); -gboolean fu_plugin_runner_update_detach (FuPlugin *plugin, +gboolean fu_plugin_runner_update_detach (FuPlugin *self, FuDevice *device, GError **error); -gboolean fu_plugin_runner_update_reload (FuPlugin *plugin, +gboolean fu_plugin_runner_update_reload (FuPlugin *self, FuDevice *device, GError **error); -gboolean fu_plugin_runner_usb_device_added (FuPlugin *plugin, - GUsbDevice *usb_device, +gboolean fu_plugin_runner_usb_device_added (FuPlugin *self, + FuUsbDevice *device, GError **error); -void fu_plugin_runner_device_register (FuPlugin *plugin, +gboolean fu_plugin_runner_udev_device_added (FuPlugin *self, + FuUdevDevice *device, + GError **error); +void fu_plugin_runner_device_removed (FuPlugin *self, + FuDevice *device); +void fu_plugin_runner_device_register (FuPlugin *self, FuDevice *device); -gboolean fu_plugin_runner_update (FuPlugin *plugin, +gboolean fu_plugin_runner_update (FuPlugin *self, FuDevice *device, - GBytes *blob_cab, GBytes *blob_fw, FwupdInstallFlags flags, GError **error); -gboolean fu_plugin_runner_verify (FuPlugin *plugin, +gboolean fu_plugin_runner_verify (FuPlugin *self, FuDevice *device, FuPluginVerifyFlags flags, GError **error); -gboolean fu_plugin_runner_unlock (FuPlugin *plugin, +gboolean fu_plugin_runner_activate (FuPlugin *self, + FuDevice *device, + GError **error); +gboolean fu_plugin_runner_unlock (FuPlugin *self, FuDevice *device, GError **error); -gboolean fu_plugin_runner_clear_results (FuPlugin *plugin, +gboolean fu_plugin_runner_clear_results (FuPlugin *self, FuDevice *device, GError **error); -gboolean fu_plugin_runner_get_results (FuPlugin *plugin, +gboolean fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error); +gboolean fu_plugin_runner_schedule_update (FuPlugin *self, + FuDevice *device, + FwupdRelease *release, + GBytes *blob_cab, + FwupdInstallFlags flags, + GError **error); gint fu_plugin_name_compare (FuPlugin *plugin1, FuPlugin *plugin2); gint fu_plugin_order_compare (FuPlugin *plugin1, @@ -107,5 +126,3 @@ gchar *fu_plugin_guess_name_from_fn (const gchar *filename); G_END_DECLS - -#endif /* __FU_PLUGIN_PRIVATE_H */ diff -Nru fwupd-1.0.9/src/fu-plugin-vfuncs.h fwupd-1.2.10/src/fu-plugin-vfuncs.h --- fwupd-1.0.9/src/fu-plugin-vfuncs.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-plugin-vfuncs.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,14 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2016-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_PLUGIN_VFUNCS_H -#define __FU_PLUGIN_VFUNCS_H +#pragma once #include "fu-plugin.h" #include "fu-device.h" +#include "fu-hash.h" G_BEGIN_DECLS @@ -34,9 +33,18 @@ FuDevice *dev, FuPluginVerifyFlags flags, GError **error); +gboolean fu_plugin_verify_attach (FuPlugin *plugin, + FuDevice *dev, + GError **error); +gboolean fu_plugin_verify_detach (FuPlugin *plugin, + FuDevice *dev, + GError **error); gboolean fu_plugin_unlock (FuPlugin *plugin, FuDevice *dev, GError **error); +gboolean fu_plugin_activate (FuPlugin *plugin, + FuDevice *dev, + GError **error); gboolean fu_plugin_clear_results (FuPlugin *plugin, FuDevice *dev, GError **error); @@ -53,9 +61,11 @@ FuDevice *dev, GError **error); gboolean fu_plugin_update_prepare (FuPlugin *plugin, + FwupdInstallFlags flags, FuDevice *dev, GError **error); gboolean fu_plugin_update_cleanup (FuPlugin *plugin, + FwupdInstallFlags flags, FuDevice *dev, GError **error); gboolean fu_plugin_composite_prepare (FuPlugin *plugin, @@ -65,11 +75,15 @@ GPtrArray *devices, GError **error); gboolean fu_plugin_usb_device_added (FuPlugin *plugin, - GUsbDevice *usb_device, + FuUsbDevice *device, + GError **error); +gboolean fu_plugin_udev_device_added (FuPlugin *plugin, + FuUdevDevice *device, + GError **error); +gboolean fu_plugin_device_removed (FuPlugin *plugin, + FuDevice *device, GError **error); void fu_plugin_device_registered (FuPlugin *plugin, FuDevice *dev); G_END_DECLS - -#endif /* __FU_PLUGIN_VFUNCS_H */ diff -Nru fwupd-1.0.9/src/fu-progressbar.c fwupd-1.2.10/src/fu-progressbar.c --- fwupd-1.0.9/src/fu-progressbar.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-progressbar.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,14 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuProgressBar" + #include "config.h" +#include #include #include "fu-progressbar.h" @@ -25,6 +27,9 @@ guint to_erase; /* chars */ guint timer_id; gint64 last_animated; /* monotonic */ + GTimer *time_elapsed; + gdouble last_estimate; + gboolean interactive; }; G_DEFINE_TYPE (FuProgressbar, fu_progressbar, G_TYPE_OBJECT) @@ -90,6 +95,53 @@ } static void +fu_progressbar_erase_line (FuProgressbar *self) +{ + if (!self->interactive) + return; + for (guint i = 0; i < self->to_erase; i++) + g_print ("\b"); + self->to_erase = 0; +} + +static gboolean +fu_progressbar_estimate_ready (FuProgressbar *self, guint percentage) +{ + gdouble old; + gdouble elapsed; + + if (percentage == 0 || percentage == 100) + return FALSE; + + old = self->last_estimate; + elapsed = g_timer_elapsed (self->time_elapsed, NULL); + self->last_estimate = elapsed / percentage * (100 - percentage); + + /* estimate is ready if we have decreased */ + return old > self->last_estimate; +} + +static gchar * +fu_progressbar_time_remaining_str (FuProgressbar *self) +{ + /* less than 5 seconds remaining */ + if (self->last_estimate < 5) + return NULL; + + /* less than 60 seconds remaining */ + if (self->last_estimate < 60) { + /* TRANSLATORS: time remaining for completing firmware flash */ + return g_strdup (_("Less than one minute remaining")); + } + + /* more than a minute */ + return g_strdup_printf (ngettext ("%.0f minute remaining", + "%.0f minutes remaining", + self->last_estimate / 60), + self->last_estimate / 60); +} + +static void fu_progressbar_refresh (FuProgressbar *self, FwupdStatus status, guint percentage) { const gchar *title; @@ -98,8 +150,7 @@ g_autoptr(GString) str = g_string_new (NULL); /* erase previous line */ - for (i = 0; i < self->to_erase; i++) - g_print ("\b"); + fu_progressbar_erase_line (self); /* add status */ if (status == FWUPD_STATUS_IDLE) { @@ -109,7 +160,7 @@ } 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 */ @@ -129,9 +180,16 @@ } g_string_append_c (str, ']'); + /* once we have good data show an estimate of time remaining */ + if (fu_progressbar_estimate_ready (self, percentage)) { + g_autofree gchar *remaining = fu_progressbar_time_remaining_str (self); + if (remaining != NULL) + g_string_append_printf (str, " %s…", remaining); + } + /* dump to screen */ g_print ("%s", str->str); - self->to_erase = str->len - 2; + self->to_erase = str->len; /* done */ if (is_idle_newline) { @@ -141,6 +199,14 @@ } } +void +fu_progressbar_set_title (FuProgressbar *self, const gchar *title) +{ + fu_progressbar_erase_line (self); + g_print ("%s\n", title); + fu_progressbar_refresh (self, self->status, self->percentage); +} + static void fu_progressbar_spin_inc (FuProgressbar *self) { @@ -181,6 +247,9 @@ if (self->timer_id != 0) { g_source_remove (self->timer_id); self->timer_id = 0; + + /* reset when the spinner has been stopped */ + g_timer_start (self->time_elapsed); } /* go back to the start when we next go into unknown percentage mode */ @@ -205,6 +274,14 @@ if (status == FWUPD_STATUS_UNKNOWN) status = self->status; + if (!self->interactive) { + if (self->status != status) { + g_debug ("%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 && @@ -237,6 +314,13 @@ } 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) { g_return_if_fail (FU_IS_PROGRESSBAR (self)); @@ -265,6 +349,8 @@ self->length_percentage = 40; self->length_status = 25; self->spinner_count_up = TRUE; + self->time_elapsed = g_timer_new (); + self->interactive = TRUE; } static void @@ -274,6 +360,7 @@ if (self->timer_id != 0) g_source_remove (self->timer_id); + g_timer_destroy (self->time_elapsed); G_OBJECT_CLASS (fu_progressbar_parent_class)->finalize (obj); } diff -Nru fwupd-1.0.9/src/fu-progressbar.h fwupd-1.2.10/src/fu-progressbar.h --- fwupd-1.0.9/src/fu-progressbar.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-progressbar.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_PROGRESSBAR_H -#define __FU_PROGRESSBAR_H +#pragma once #include @@ -25,8 +23,9 @@ guint len); 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 - -#endif /* __FU_PROGRESSBAR_H */ - diff -Nru fwupd-1.0.9/src/fu-quirks.c fwupd-1.2.10/src/fu-quirks.c --- fwupd-1.0.9/src/fu-quirks.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-quirks.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,20 +1,22 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes +/* + * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuQuirks" + #include "config.h" #include #include -#include #include #include "fu-common.h" +#include "fu-mutex.h" #include "fu-quirks.h" +#include "fwupd-common.h" #include "fwupd-error.h" #include "fwupd-remote-private.h" @@ -49,7 +51,8 @@ { GObject parent_instance; GPtrArray *monitors; - GHashTable *hash; /* of prefix/id:string */ + GHashTable *hash; /* of group:{key:value} */ + GRWLock hash_mutex; }; G_DEFINE_TYPE (FuQuirks, fu_quirks, G_TYPE_OBJECT) @@ -85,11 +88,30 @@ return TRUE; } +static gchar * +fu_quirks_build_group_key (const gchar *group) +{ + const gchar *guid_prefixes[] = { "DeviceInstanceId=", "Guid=", "HwId=", NULL }; + + /* this is a GUID */ + 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 (fwupd_guid_is_valid (group + len)) + return g_strdup (group + len); + return fwupd_guid_hash_string (group + len); + } + } + + /* fallback */ + return g_strdup (group); +} + /** * fu_quirks_lookup_by_id: * @self: A #FuPlugin - * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks" - * @id: An ID to match the entry, e.g. "012345" + * @group: A string group, e.g. "DeviceInstanceId=USB\VID_1235&PID_AB11" + * @key: An ID to match the entry, e.g. "Name" * * Looks up an entry in the hardware database using a string value. * @@ -98,102 +120,116 @@ * Since: 1.0.1 **/ const gchar * -fu_quirks_lookup_by_id (FuQuirks *self, const gchar *prefix, const gchar *id) +fu_quirks_lookup_by_id (FuQuirks *self, const gchar *group, const gchar *key) { - g_autofree gchar *key = NULL; + GHashTable *kvs; + g_autofree gchar *group_key = NULL; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->hash_mutex); g_return_val_if_fail (FU_IS_QUIRKS (self), NULL); - g_return_val_if_fail (prefix != NULL, NULL); - g_return_val_if_fail (id != NULL, NULL); - - key = g_strdup_printf ("%s/%s", prefix, id); - return g_hash_table_lookup (self->hash, key); + g_return_val_if_fail (group != NULL, NULL); + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (locker != NULL, NULL); + + group_key = fu_quirks_build_group_key (group); + kvs = g_hash_table_lookup (self->hash, group_key); + if (kvs == NULL) + return NULL; + return g_hash_table_lookup (kvs, key); } /** - * fu_quirks_lookup_by_glob: + * fu_quirks_get_kvs_for_guid: * @self: A #FuPlugin - * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks" - * @glob: An glob to match the entry, e.g. "foo*bar?baz" + * @guid: a GUID + * @iter: A #GHashTableIter, typically allocated on the stack by the caller * - * Looks up an entry in the hardware database using a key glob. - * NOTE: This is *much* slower than using fu_quirks_lookup_by_id() as each key - * in the quirk database is compared. + * Looks up all entries in the hardware database using a GUID value. * - * Returns: (transfer none): values from the database, or %NULL if not found + * Returns: %TRUE if the GUID was found, and @iter was set * - * Since: 1.0.1 + * Since: 1.1.2 **/ -const gchar * -fu_quirks_lookup_by_glob (FuQuirks *self, const gchar *prefix, const gchar *glob) +gboolean +fu_quirks_get_kvs_for_guid (FuQuirks *self, const gchar *guid, GHashTableIter *iter) { - g_autoptr(GList) keys = NULL; - gsize prefix_len; - - g_return_val_if_fail (FU_IS_QUIRKS (self), NULL); - g_return_val_if_fail (prefix != NULL, NULL); - g_return_val_if_fail (glob != NULL, NULL); + GHashTable *kvs; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->hash_mutex); + g_return_val_if_fail (locker != NULL, FALSE); + kvs = g_hash_table_lookup (self->hash, guid); + if (kvs == NULL) + return FALSE; + g_hash_table_iter_init (iter, kvs); + return TRUE; +} - prefix_len = strlen (prefix); - keys = g_hash_table_get_keys (self->hash); - for (GList *l = keys; l != NULL; l = l->next) { - const gchar *id = l->data; - if (strncmp (id, prefix, prefix_len) != 0) - continue; - id += prefix_len + 1; - if (fnmatch (glob, id, 0) == 0) - return fu_quirks_lookup_by_id (self, prefix, id); - if (fnmatch (id, glob, 0) == 0) - return fu_quirks_lookup_by_id (self, prefix, id); +static gchar * +fu_quirks_merge_values (const gchar *old, const gchar *new) +{ + guint cnt = 0; + g_autofree gchar **resv = NULL; + g_auto(GStrv) newv = g_strsplit (new, ",", -1); + g_auto(GStrv) oldv = g_strsplit (old, ",", -1); + + /* segment flags, and append if they do not already exists */ + resv = g_new0 (gchar *, g_strv_length (oldv) + g_strv_length (newv) + 1); + for (guint i = 0; oldv[i] != NULL; i++) { + if (!g_strv_contains ((const gchar * const *) resv, oldv[i])) + resv[cnt++] = oldv[i]; } - return NULL; + for (guint i = 0; newv[i] != NULL; i++) { + if (!g_strv_contains ((const gchar * const *) resv, newv[i])) + resv[cnt++] = newv[i]; + } + return g_strjoinv (",", resv); } /** - * fu_quirks_lookup_by_usb_device: - * @self: A #FuPlugin - * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks" - * @dev: A #GUsbDevice - * - * Looks up an entry in the hardware database using various keys generated - * from @dev. + * fu_quirks_add_value: (skip) + * @self: A #FuQuirks + * @group: group, e.g. `DeviceInstanceId=USB\VID_0BDA&PID_1100` + * @key: group, e.g. `Name` + * @value: group, e.g. `Unknown Device` * - * Returns: (transfer none): values from the database, or %NULL if not found + * Adds a value to the quirk database. Normally this is achieved by loading a + * quirk file using fu_quirks_load(). * - * Since: 1.0.1 + * Since: 1.1.2 **/ -const gchar * -fu_quirks_lookup_by_usb_device (FuQuirks *self, const gchar *prefix, GUsbDevice *dev) +void +fu_quirks_add_value (FuQuirks *self, const gchar *group, const gchar *key, const gchar *value) { - const gchar *tmp; - g_autofree gchar *key1 = NULL; - g_autofree gchar *key2 = NULL; - g_autofree gchar *key3 = NULL; - - g_return_val_if_fail (FU_IS_QUIRKS (self), NULL); - g_return_val_if_fail (prefix != NULL, NULL); - g_return_val_if_fail (G_USB_IS_DEVICE (dev), NULL); + GHashTable *kvs; + const gchar *value_old; + g_autofree gchar *group_key = NULL; + g_autofree gchar *value_new = NULL; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_writer_locker_new (&self->hash_mutex); + + g_return_if_fail (locker != NULL); + + /* does the key already exists in our hash */ + group_key = fu_quirks_build_group_key (group); + kvs = g_hash_table_lookup (self->hash, group_key); + if (kvs == NULL) { + kvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert (self->hash, + g_steal_pointer (&group_key), + kvs); + value_new = g_strdup (value); + } else { + /* look up in the 2nd level hash */ + value_old = g_hash_table_lookup (kvs, key); + if (value_old != NULL) { + g_debug ("already found %s=%s, merging with %s", + group_key, value_old, value); + value_new = fu_quirks_merge_values (value_old, value); + } else { + value_new = g_strdup (value); + } + } - /* prefer an exact match, VID:PID:REV */ - key1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&REV_%04X", - g_usb_device_get_vid (dev), - g_usb_device_get_pid (dev), - g_usb_device_get_release (dev)); - tmp = fu_quirks_lookup_by_id (self, prefix, key1); - if (tmp != NULL) - return tmp; - - /* VID:PID */ - key2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", - g_usb_device_get_vid (dev), - g_usb_device_get_pid (dev)); - tmp = fu_quirks_lookup_by_id (self, prefix, key2); - if (tmp != NULL) - return tmp; - - /* VID */ - key3 = g_strdup_printf ("USB\\VID_%04X", g_usb_device_get_vid (dev)); - return fu_quirks_lookup_by_id (self, prefix, key3); + /* insert the new value */ + g_hash_table_insert (kvs, g_strdup (key), g_steal_pointer (&value_new)); } static gboolean @@ -214,16 +250,14 @@ if (keys == NULL) return FALSE; for (guint j = 0; keys[j] != NULL; j++) { - g_autofree gchar *tmp = NULL; - tmp = g_key_file_get_value (kf, groups[i], keys[j], error); - if (tmp == NULL) + g_autofree gchar *value = NULL; + /* get value from keyfile */ + value = g_key_file_get_value (kf, groups[i], keys[j], error); + if (value == NULL) return FALSE; - g_hash_table_insert (self->hash, - g_strdup_printf ("%s/%s", groups[i], keys[j]), - g_steal_pointer (&tmp)); + fu_quirks_add_value (self, groups[i], keys[j], value); } } - g_debug ("now %u quirk entries", g_hash_table_size (self->hash)); return TRUE; } @@ -280,6 +314,7 @@ } /* success */ + g_debug ("now %u quirk entries", g_hash_table_size (self->hash)); return TRUE; } @@ -299,11 +334,14 @@ { g_autofree gchar *datadir = NULL; g_autofree gchar *localstatedir = NULL; + g_return_val_if_fail (FU_IS_QUIRKS (self), FALSE); /* ensure empty in case we're called from a monitor change */ g_ptr_array_set_size (self->monitors, 0); + g_rw_lock_writer_lock (&self->hash_mutex); g_hash_table_remove_all (self->hash); + g_rw_lock_writer_unlock (&self->hash_mutex); /* system datadir */ datadir = fu_common_get_path (FU_PATH_KIND_DATADIR_PKG); @@ -330,7 +368,8 @@ fu_quirks_init (FuQuirks *self) { self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - self->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + self->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref); + g_rw_lock_init (&self->hash_mutex); } static void @@ -338,6 +377,7 @@ { FuQuirks *self = FU_QUIRKS (obj); g_ptr_array_unref (self->monitors); + g_rw_lock_clear (&self->hash_mutex); g_hash_table_unref (self->hash); G_OBJECT_CLASS (fu_quirks_parent_class)->finalize (obj); } diff -Nru fwupd-1.0.9/src/fu-quirks.h fwupd-1.2.10/src/fu-quirks.h --- fwupd-1.0.9/src/fu-quirks.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-quirks.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,18 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016 Mario Limonciello +/* + * Copyright (C) 2016 Mario Limonciello * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_QUIRKS_H -#define __FU_QUIRKS_H - -G_BEGIN_DECLS +#pragma once #include -#include + +G_BEGIN_DECLS #define FU_TYPE_QUIRKS (fu_quirks_get_type ()) G_DECLARE_FINAL_TYPE (FuQuirks, fu_quirks, FU, QUIRKS, GObject) @@ -21,213 +18,34 @@ gboolean fu_quirks_load (FuQuirks *self, GError **error); const gchar *fu_quirks_lookup_by_id (FuQuirks *self, - const gchar *prefix, - const gchar *id); -const gchar *fu_quirks_lookup_by_glob (FuQuirks *self, - const gchar *prefix, - const gchar *glob); -const gchar *fu_quirks_lookup_by_usb_device (FuQuirks *self, - const gchar *prefix, - GUsbDevice *dev); - -/** - * FU_QUIRKS_DFU: - * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` - * @value: a string, separated using `|`, e.g. `ignore-polltimeout|no-pid-change` - * - * Assigns optional quirks to use for a DFU device which does not follow the - * DFU 1.0 or 1.1 specification. The list of supported quirks is thus: - * - * * `none`: No device quirks - * * `action-required`: User has to do something manually, e.g. press a button - * * `attach-extra-reset`: Device needs resetting twice for attach - * * `attach-upload-download`: An upload or download is required for attach - * * `force-dfu-mode`: Force DFU mode - * * `ignore-polltimeout`: Ignore the device download timeout - * * `ignore-runtime`: Device has broken DFU runtime support - * * `ignore-upload`: Uploading from the device is broken - * * `no-dfu-runtime`: No DFU runtime interface is provided - * * `no-get-status-upload`: Do not do GetStatus when uploading - * * `no-pid-change`: Accept the same VID:PID when changing modes - * * `use-any-interface`: Use any interface for DFU - * * `use-atmel-avr`: Device uses the ATMEL bootloader - * * `use-protocol-zero`: Fix up the protocol number - * * `legacy-protocol`: Use a legacy protocol version - * - * Default value: `none` - * - * Since: 1.0.1 - */ -#define FU_QUIRKS_DFU "fwupd-dfu" - -/** - * 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 "fwupd-uefi-version-format" - -/** - * 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 "fwupd-daemon-version-format" - -/** - * FU_QUIRKS_DFU_JABRA_DETACH: - * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` - * @value: the two uint8_t unlock values, encoded in base 16, e.g. `0201` - * - * Assigns the two magic bytes sent to the Jabra hardware when the device is - * in runtime mode to make it switch into DFU mode. - * - * Since: 1.0.1 - */ -#define FU_QUIRKS_DFU_JABRA_DETACH "fwupd-dfu-jabra-detach" - -/** - * FU_QUIRKS_DFU_AVR_CHIP_ID: - * @key: the AVR chip ID, e.g. `0x58200204` - * @value: the UM0424 sector description, e.g. `@Flash/0x2000/1*248Kg` - * - * Assigns a sector description for the chip ID. This is required so fwupd can - * program the user firmware avoiding the bootloader and for checking the total - * element size. - * - * The chip ID can be found from a datasheet or using `dfu-tool list` when the - * hardware is connected and in bootloader mode. - * - * Since: 1.0.1 - */ -#define FU_QUIRKS_DFU_AVR_CHIP_ID "fwupd-dfu-avr-chip-id" - -/** - * FU_QUIRKS_DFU_FORCE_VERSION: - * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` - * @value: the uint16_t DFU version, encoded in base 16, e.g. `0110` - * - * 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. - * - * Since: 1.0.1 - */ -#define FU_QUIRKS_DFU_FORCE_VERSION "fwupd-dfu-force-version" - -/** - * FU_QUIRKS_USB_SUMMARY: - * @key: the USB device ID, e.g. `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_USB_SUMMARY "FuUsbDevice:summary" - -/** - * FU_QUIRKS_USB_ICON: - * @key: the USB device ID, e.g. `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_USB_ICON "FuUsbDevice:icon" - -/** - * FU_QUIRKS_USB_NAME: - * @key: the USB device ID, e.g. `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_USB_NAME "FuUsbDevice:name" - -/** - * FU_QUIRKS_USB_GUID: - * @key: the USB device ID, e.g. `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_USB_GUID "FuUsbDevice:guid" - -/** - * FU_QUIRKS_USB_VERSION: - * @key: the USB device ID, e.g. `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_USB_VERSION "FuUsbDevice:version" - -/** - * FU_QUIRKS_USB_VENDOR: - * @key: the USB device ID, e.g. `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_USB_VENDOR "FuUsbDevice:vendor" - -/** - * FU_QUIRKS_CSR: - * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` - * @value: the quirk, e.g. `require-delay` - * - * Assigns optional quirks to use for a CSR device which does not follow the - * CSR specification. The list of supported quirks is thus: - * - * * `none`: No device quirks - * * `require-delay`: Respect the write timeout value - * - * Since: 1.0.3 - */ -#define FU_QUIRKS_CSR_DEVICE "FuCsrDevice" - -/** - * FU_QUIRKS_EBITDO: - * @key: the USB device ID, e.g. `USB\VID_0763&PID_2806` - * @value: the quirk, e.g. `bootloader` - * - * Assigns optional quirks to use for a 8Bitdo device. The list of supported - * quirks is thus: - * - * * `none`: No device quirks - * * `bootloader`: Device is in bootloader mode - * - * Since: 1.0.3 - */ -#define FU_QUIRKS_EBITDO_DEVICE "FuEditdoDevice" + const gchar *group, + const gchar *key); +void fu_quirks_add_value (FuQuirks *self, + const gchar *group, + const gchar *key, + const gchar *value); +gboolean fu_quirks_get_kvs_for_guid (FuQuirks *self, + const gchar *guid, + GHashTableIter *iter); + +#define FU_QUIRKS_PLUGIN "Plugin" +#define FU_QUIRKS_UEFI_VERSION_FORMAT "UefiVersionFormat" +#define FU_QUIRKS_DAEMON_VERSION_FORMAT "ComponentIDs" +#define FU_QUIRKS_FLAGS "Flags" +#define FU_QUIRKS_SUMMARY "Summary" +#define FU_QUIRKS_ICON "Icon" +#define FU_QUIRKS_NAME "Name" +#define FU_QUIRKS_GUID "Guid" +#define FU_QUIRKS_COUNTERPART_GUID "CounterpartGuid" +#define FU_QUIRKS_PARENT_GUID "ParentGuid" +#define FU_QUIRKS_CHILDREN "Children" +#define FU_QUIRKS_VERSION "Version" +#define FU_QUIRKS_VENDOR "Vendor" +#define FU_QUIRKS_VENDOR_ID "VendorId" +#define FU_QUIRKS_FIRMWARE_SIZE_MIN "FirmwareSizeMin" +#define FU_QUIRKS_FIRMWARE_SIZE_MAX "FirmwareSizeMax" +#define FU_QUIRKS_FIRMWARE_SIZE "FirmwareSize" +#define FU_QUIRKS_INSTALL_DURATION "InstallDuration" +#define FU_QUIRKS_VERSION_FORMAT "VersionFormat" G_END_DECLS - -#endif /* __FU_QUIRKS_H */ diff -Nru fwupd-1.0.9/src/fu-self-test.c fwupd-1.2.10/src/fu-self-test.c --- fwupd-1.0.9/src/fu-self-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-self-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ @@ -7,7 +6,7 @@ #include "config.h" -#include +#include #include #include #include @@ -16,7 +15,10 @@ #include #include +#include "fu-archive.h" #include "fu-common-cab.h" +#include "fu-common-version.h" +#include "fu-chunk.h" #include "fu-config.h" #include "fu-device-list.h" #include "fu-device-private.h" @@ -28,6 +30,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" @@ -40,27 +43,160 @@ #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, FU_ENGINE_LOAD_FLAG_NONE, &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", FWUPD_VERSION_FORMAT_TRIPLET); + 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), ==, FWUPD_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format (""), ==, FWUPD_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1234ac"), ==, FWUPD_VERSION_FORMAT_PLAIN); + g_assert_cmpint (fu_common_version_guess_format ("1.2"), ==, FWUPD_VERSION_FORMAT_PAIR); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3"), ==, FWUPD_VERSION_FORMAT_TRIPLET); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3.4"), ==, FWUPD_VERSION_FORMAT_QUAD); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3.4.5"), ==, FWUPD_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1a.2b.3"), ==, FWUPD_VERSION_FORMAT_PLAIN); + g_assert_cmpint (fu_common_version_guess_format ("1"), ==, FWUPD_VERSION_FORMAT_NUMBER); + g_assert_cmpint (fu_common_version_guess_format ("0x10201"), ==, FWUPD_VERSION_FORMAT_NUMBER); +} + +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 @@ 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); @@ -97,28 +241,141 @@ } static void +fu_engine_requirements_child_func (void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuDevice) child = 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 = + "" + " " + " not-child" + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version (device, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_bootloader (device, "4.5.6"); + fu_device_set_vendor_id (device, "FFFF"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version (child, "0.0.999", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_child (device, child); + + /* make the component require three things */ + silo = xb_silo_new_from_xml (xml, &error); + g_assert_no_error (error); + g_assert_nonnull (silo); + component = xb_silo_query_first (silo, "component", &error); + g_assert_no_error (error); + g_assert_nonnull (component); + + /* check this passes */ + task = fu_install_task_new (device, component); + ret = fu_engine_check_requirements (engine, task, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error (error); + g_assert (ret); +} + +static void +fu_engine_requirements_child_fail_func (void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuDevice) child = 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 = + "" + " " + " not-child" + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version (device, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_bootloader (device, "4.5.6"); + fu_device_set_vendor_id (device, "FFFF"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version (child, "0.0.1", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_child (device, child); + + /* make the component require three things */ + silo = xb_silo_new_from_xml (xml, &error); + g_assert_no_error (error); + g_assert_nonnull (silo); + component = xb_silo_query_first (silo, "component", &error); + g_assert_no_error (error); + g_assert_nonnull (component); + + /* check this passes */ + task = fu_install_task_new (device, component); + ret = fu_engine_check_requirements (engine, task, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_nonnull (g_strstr_len (error->message, -1, + "Not compatible with child device version")); + g_assert (!ret); +} + +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,55 +387,46 @@ 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"); + fu_device_set_version (device, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version_bootloader (device, "4.5.6"); fu_device_set_vendor_id (device, "FFFF"); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); 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); @@ -187,28 +435,196 @@ } static void +fu_engine_requirements_version_format_func (void) +{ + gboolean ret; + 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 = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + " " + " triplet" + " " + ""; + + /* set up a dummy device */ + fu_device_set_version (device, "1.2.3.4", FWUPD_VERSION_FORMAT_QUAD); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); + + /* make the component require three things */ + silo = xb_silo_new_from_xml (xml, &error); + g_assert_no_error (error); + g_assert_nonnull (silo); + component = xb_silo_query_first (silo, "component", &error); + g_assert_no_error (error); + g_assert_nonnull (component); + + /* check this fails */ + task = fu_install_task_new (device, component); + ret = fu_engine_check_requirements (engine, task, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_nonnull (g_strstr_len (error->message, -1, + "Firmware version formats were different")); + g_assert (!ret); +} + +static void +fu_engine_requirements_other_device_func (void) +{ + gboolean ret; + 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", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag (device1, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_guid (device1, "12345678-1234-1234-1234-123456789012"); + + /* set up a different device */ + fu_device_set_id (device2, "id2"); + fu_device_set_name (device2, "Secondary firmware"); + fu_device_set_version (device2, "4.5.6", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_vendor_id (device2, "FFFF"); + fu_device_add_guid (device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b"); + fu_engine_add_device (engine, device2); + + /* 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, component); + ret = fu_engine_check_requirements (engine, task, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error (error); + g_assert (ret); +} + +static void +fu_engine_device_priority_func (void) +{ + g_autoptr(FuDevice) device1 = fu_device_new (); + g_autoptr(FuDevice) device2 = fu_device_new (); + g_autoptr(FuDevice) device3 = fu_device_new (); + g_autoptr(FuDevice) device = NULL; + g_autoptr(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"); + fu_device_set_priority (device1, 0); + fu_device_set_plugin (device1, "udev"); + fu_device_add_instance_id (device1, "GUID1"); + fu_device_convert_instance_ids (device1); + fu_engine_add_device (engine, device1); + fu_device_set_id (device2, "id2"); + fu_device_set_priority (device2, 1); + fu_device_set_plugin (device2, "redfish"); + fu_device_add_instance_id (device2, "GUID1"); + fu_device_convert_instance_ids (device2); + fu_engine_add_device (engine, device2); + fu_device_set_id (device3, "id3"); + fu_device_set_priority (device3, 0); + fu_device_set_plugin (device3, "uefi"); + fu_device_add_instance_id (device3, "GUID1"); + fu_device_convert_instance_ids (device3); + fu_engine_add_device (engine, device3); + + /* get the high prio device */ + device = fu_engine_get_device (engine, "867d5f8110f8aa79dd63d7440f21724264f10430", &error); + g_assert_no_error (error); + g_assert_cmpint (fu_device_get_priority (device), ==, 1); + g_clear_object (&device); + + /* the now-removed low-prio device */ + device = fu_engine_get_device (engine, "4e89d81a2e6fb4be2578d245fd8511c1f4ad0b58", &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null (device); + g_clear_error (&error); + + /* the never-added 2nd low-prio device */ + device = fu_engine_get_device (engine, "c48feddbbcfee514f530ce8f7f2dccd98b6cc150", &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null (device); +} + +static void fu_engine_device_parent_func (void) { g_autoptr(FuDevice) device1 = fu_device_new (); 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"); - fu_device_add_guid (device1, "child-GUID-1"); + fu_device_add_instance_id (device1, "child-GUID-1"); fu_device_add_parent_guid (device1, "parent-GUID"); + fu_device_convert_instance_ids (device1); fu_engine_add_device (engine, device1); /* parent */ fu_device_set_id (device2, "parent"); - fu_device_add_guid (device2, "parent-GUID"); + fu_device_add_instance_id (device2, "parent-GUID"); fu_device_set_vendor (device2, "oem"); + fu_device_convert_instance_ids (device2); /* add another child */ fu_device_set_id (device3, "child2"); - fu_device_add_guid (device3, "child-GUID-2"); + fu_device_add_instance_id (device3, "child-GUID-2"); fu_device_add_parent_guid (device3, "parent-GUID"); + fu_device_convert_instance_ids (device3); fu_device_add_child (device2, device3); /* add two together */ @@ -237,9 +653,14 @@ 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 */ @@ -286,17 +707,62 @@ } static void +fu_engine_device_unlock_func (void) +{ + 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(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, FU_ENGINE_LOAD_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + + /* add the hardcoded 'fwupd' metadata */ + filename = fu_test_get_filename (TESTDATADIR, "metadata.xml"); + g_assert (filename != NULL); + 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"); + fu_device_add_guid (device, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_LOCKED); + fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_PLAIN); + fu_engine_add_device (engine, device); + + /* ensure the metainfo was matched */ + g_assert_nonnull (fwupd_device_get_release_default (FWUPD_DEVICE (device))); +} + +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 */ @@ -304,8 +770,11 @@ 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); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); g_assert_no_error (error); g_assert (ret); @@ -315,23 +784,24 @@ 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"); - fu_device_set_version (device, "1.2.2"); + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); 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); @@ -357,6 +827,13 @@ 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", @@ -424,14 +901,10 @@ g_assert_no_error (error); g_assert (ret); - /* expect just one broken remote to fail */ - g_test_expect_message (G_LOG_DOMAIN, 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); - ret = fu_engine_load (engine, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); @@ -441,7 +914,7 @@ 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); @@ -450,7 +923,7 @@ g_clear_error (&error); /* add a device so we can get upgrades and downgrades */ - fu_device_set_version (device, "1.2.3"); + fu_device_set_version (device, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_id (device, "test_device"); fu_device_set_name (device, "Test Device"); fu_device_add_guid (device, "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); @@ -469,6 +942,16 @@ g_assert (releases != NULL); g_assert_cmpint (releases->len, ==, 4); + /* no upgrades, as no firmware is approved */ + releases_up = fu_engine_get_upgrades (engine, fu_device_get_id (device), &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO); + g_assert_null (releases_up); + g_clear_error (&error); + + /* retry with approved firmware set */ + fu_engine_add_approved_firmware (engine, "deadbeefdeadbeefdeadbeefdeadbeef"); + fu_engine_add_approved_firmware (engine, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + /* upgrades */ releases_up = fu_engine_get_upgrades (engine, fu_device_get_id (device), &error); g_assert_no_error (error); @@ -491,27 +974,102 @@ } 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, FU_ENGINE_LOAD_FLAG_NONE, &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", FWUPD_VERSION_FORMAT_TRIPLET); + 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); @@ -522,17 +1080,18 @@ 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); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); /* add a device so we can get upgrade it */ - fu_device_set_version (device, "1.2.2"); + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_id (device, "test_device"); 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); @@ -547,21 +1106,29 @@ 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); + + /* set the counter */ + g_setenv ("FWUPD_PLUGIN_TEST", "another-write-required", TRUE); + fu_device_set_metadata_integer (device, "nr-update", 0); /* 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); g_assert (ret); + /* check the write was done more than once */ + g_assert_cmpint (fu_device_get_metadata_integer (device, "nr-update"), ==, 2); + /* check the history database */ history = fu_history_new (); device2 = fu_history_get_device_by_id (history, fu_device_get_id (device), &error); @@ -587,7 +1154,7 @@ " [Release]\n" " Version: 1.2.3\n" " Checksum: SHA1(%s)\n" - " TrustFlags: none\n", + " Flags: none\n", checksum); ret = fu_test_compare_lines (device_str, device_str_expected, &error); g_assert_no_error (error); @@ -613,16 +1180,118 @@ } static void +fu_engine_history_inherit (void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + 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(FuInstallTask) task = NULL; + g_autoptr(FuPlugin) plugin = fu_plugin_new (); + 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; + + /* no metadata in daemon */ + fu_engine_set_silo (engine, silo_empty); + + /* set up dummy plugin */ + g_setenv ("FWUPD_PLUGIN_TEST", "fail", TRUE); + ret = fu_plugin_open (plugin, PLUGINBUILDDIR "/libfu_plugin_test.so", &error); + g_assert_no_error (error); + g_assert (ret); + fu_engine_add_plugin (engine, plugin); + testdatadir = fu_test_get_filename (TESTDATADIR, "."); + g_assert (testdatadir != NULL); + g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); + + /* add a device so we can get upgrade it */ + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_id (device, "test_device"); + 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_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_created (device, 1515338000); + 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_REGISTERED)); + + filename = fu_test_get_filename (TESTDATADIR, "missing-hwid/noreqs-1.2.3.cab"); + g_assert (filename != NULL); + blob_cab = fu_common_get_contents_bytes (filename, &error); + g_assert_no_error (error); + g_assert (blob_cab != NULL); + silo = fu_engine_get_silo_from_blob (engine, blob_cab, &error); + g_assert_no_error (error); + g_assert_nonnull (silo); + + /* 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 */ + g_setenv ("FWUPD_PLUGIN_TEST", "requires-activation", TRUE); + 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); + g_assert (ret); + + /* check the device requires an activation */ + g_assert_true (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)); + g_assert_cmpstr (fu_device_get_version (device), ==, "1.2.2"); + + /* activate the device */ + ret = fu_engine_activate (engine, fu_device_get_id (device), &error); + g_assert_no_error (error); + g_assert (ret); + + /* check the device no longer requires an activation */ + g_assert_false (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)); + g_assert_cmpstr (fu_device_get_version (device), ==, "1.2.3"); + + /* emulate getting the flag for a fresh boot on old firmware */ + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); + ret = fu_engine_install (engine, task, blob_cab, + FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + g_object_unref (engine); + g_object_unref (device); + engine = fu_engine_new (FU_APP_FLAGS_NONE); + fu_engine_set_silo (engine, silo_empty); + fu_engine_add_plugin (engine, plugin); + device = fu_device_new (); + fu_device_set_id (device, "test_device"); + fu_device_set_name (device, "Test Device"); + fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); + fu_engine_add_device (engine, device); + g_assert_true (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)); +} + +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); @@ -633,6 +1302,12 @@ 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); @@ -644,13 +1319,13 @@ 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); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); /* add a device so we can get upgrade it */ - fu_device_set_version (device, "1.2.2"); + fu_device_set_version (device, "1.2.2", FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_id (device, "test_device"); fu_device_set_name (device, "Test Device"); fu_device_set_plugin (device, "test"); @@ -670,12 +1345,13 @@ 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_nonnull (silo); + component = xb_silo_query_first (silo, "components/component/id[text()='com.hughski.test.firmware']/..", &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 (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); @@ -711,7 +1387,7 @@ " [Release]\n" " Version: 1.2.3\n" " Checksum: SHA1(%s)\n" - " TrustFlags: none\n", + " Flags: none\n", checksum); ret = fu_test_compare_lines (device_str, device_str_expected, &error); g_assert_no_error (error); @@ -747,8 +1423,9 @@ /* add one device */ fu_device_set_id (device1, "device1"); - fu_device_add_guid (device1, "foobar"); + fu_device_add_instance_id (device1, "foobar"); fu_device_set_remove_delay (device1, 100); + fu_device_convert_instance_ids (device1); fu_device_list_add (device_list, device1); g_assert_cmpint (added_cnt, ==, 1); g_assert_cmpint (removed_cnt, ==, 0); @@ -780,11 +1457,142 @@ g_assert_cmpint (changed_cnt, ==, 1); } +typedef struct { + FuDevice *device_new; + FuDevice *device_old; + FuDeviceList *device_list; +} FuDeviceListReplugHelper; + +static gboolean +fu_device_list_remove_cb (gpointer user_data) +{ + FuDeviceListReplugHelper *helper = (FuDeviceListReplugHelper *) user_data; + fu_device_list_remove (helper->device_list, helper->device_old); + return FALSE; +} + +static gboolean +fu_device_list_add_cb (gpointer user_data) +{ + FuDeviceListReplugHelper *helper = (FuDeviceListReplugHelper *) user_data; + fu_device_list_add (helper->device_list, helper->device_new); + return FALSE; +} + +static void +fu_device_list_replug_auto_func (void) +{ + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new (); + g_autoptr(FuDevice) device2 = fu_device_new (); + g_autoptr(FuDevice) parent = fu_device_new (); + g_autoptr(FuDeviceList) device_list = fu_device_list_new (); + g_autoptr(GError) error = NULL; + FuDeviceListReplugHelper helper; + + /* parent */ + fu_device_set_id (parent, "parent"); + + /* fake child devices */ + fu_device_set_id (device1, "device1"); + fu_device_set_physical_id (device1, "ID"); + fu_device_set_plugin (device1, "self-test"); + fu_device_set_remove_delay (device1, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_child (parent, device1); + fu_device_set_id (device2, "device2"); + fu_device_set_physical_id (device2, "ID"); /* matches */ + fu_device_set_plugin (device2, "self-test"); + fu_device_set_remove_delay (device2, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + + /* not yet added */ + ret = fu_device_list_wait_for_replug (device_list, device1, &error); + g_assert_no_error (error); + g_assert (ret); + + /* add device */ + fu_device_list_add (device_list, device1); + + /* not waiting */ + ret = fu_device_list_wait_for_replug (device_list, device1, &error); + g_assert_no_error (error); + g_assert (ret); + + /* waiting */ + helper.device_old = device1; + helper.device_new = device2; + helper.device_list = device_list; + g_timeout_add (100, fu_device_list_remove_cb, &helper); + g_timeout_add (200, fu_device_list_add_cb, &helper); + fu_device_add_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + ret = fu_device_list_wait_for_replug (device_list, device1, &error); + g_assert_no_error (error); + g_assert (ret); + + /* check device2 now has parent too */ + g_assert (fu_device_get_parent (device2) == parent); + + /* waiting, failed */ + fu_device_add_flag (device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + ret = fu_device_list_wait_for_replug (device_list, device2, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert (!ret); +} + +static void +fu_device_list_replug_user_func (void) +{ + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new (); + g_autoptr(FuDevice) device2 = fu_device_new (); + g_autoptr(FuDeviceList) device_list = fu_device_list_new (); + g_autoptr(GError) error = NULL; + FuDeviceListReplugHelper helper; + + /* fake devices */ + fu_device_set_id (device1, "device1"); + fu_device_add_instance_id (device1, "foo"); + fu_device_add_instance_id (device1, "bar"); + fu_device_set_plugin (device1, "self-test"); + fu_device_set_remove_delay (device1, FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + fu_device_convert_instance_ids (device1); + fu_device_set_id (device2, "device2"); + fu_device_add_instance_id (device2, "baz"); + fu_device_add_instance_id (device2, "bar"); /* matches */ + fu_device_set_plugin (device2, "self-test"); + fu_device_set_remove_delay (device2, FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + fu_device_convert_instance_ids (device2); + + /* not yet added */ + ret = fu_device_list_wait_for_replug (device_list, device1, &error); + g_assert_no_error (error); + g_assert (ret); + + /* add device */ + fu_device_list_add (device_list, device1); + + /* not waiting */ + ret = fu_device_list_wait_for_replug (device_list, device1, &error); + g_assert_no_error (error); + g_assert (ret); + + /* waiting */ + helper.device_old = device1; + helper.device_new = device2; + helper.device_list = device_list; + g_timeout_add (100, fu_device_list_remove_cb, &helper); + g_timeout_add (200, fu_device_list_add_cb, &helper); + fu_device_add_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + ret = fu_device_list_wait_for_replug (device_list, device1, &error); + g_assert_no_error (error); + g_assert (ret); +} + static void fu_device_list_compatible_func (void) { g_autoptr(FuDevice) device1 = fu_device_new (); g_autoptr(FuDevice) device2 = fu_device_new (); + g_autoptr(FuDevice) device_old = NULL; g_autoptr(FuDeviceList) device_list = fu_device_list_new (); g_autoptr(GPtrArray) devices_all = NULL; g_autoptr(GPtrArray) devices_active = NULL; @@ -807,10 +1615,11 @@ fu_device_set_id (device1, "device1"); fu_device_set_plugin (device1, "plugin-for-runtime"); fu_device_set_vendor_id (device1, "USB:0x20A0"); - fu_device_set_version (device1, "1.2.3"); - fu_device_add_guid (device1, "foobar"); - fu_device_add_guid (device1, "bootloader"); + fu_device_set_version (device1, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_instance_id (device1, "foobar"); + fu_device_add_instance_id (device1, "bootloader"); fu_device_set_remove_delay (device1, 100); + fu_device_convert_instance_ids (device1); fu_device_list_add (device_list, device1); g_assert_cmpint (added_cnt, ==, 1); g_assert_cmpint (removed_cnt, ==, 0); @@ -819,7 +1628,8 @@ /* add another device in bootloader mode */ fu_device_set_id (device2, "device2"); fu_device_set_plugin (device2, "plugin-for-bootloader"); - fu_device_add_guid (device2, "bootloader"); + fu_device_add_instance_id (device2, "bootloader"); + fu_device_convert_instance_ids (device2); /* verify only a changed event was generated */ added_cnt = removed_cnt = changed_cnt = 0; @@ -851,8 +1661,55 @@ "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a"); /* verify we can get the old device from the new device */ - device = fu_device_list_get_old (device_list, device2); - g_assert (device == device1); + device_old = fu_device_list_get_old (device_list, device2); + g_assert (device_old == device1); +} + +static void +fu_device_list_remove_chain_func (void) +{ + g_autoptr(FuDeviceList) device_list = fu_device_list_new (); + g_autoptr(FuDevice) device_child = fu_device_new (); + g_autoptr(FuDevice) device_parent = fu_device_new (); + + guint added_cnt = 0; + guint changed_cnt = 0; + guint removed_cnt = 0; + + g_signal_connect (device_list, "added", + G_CALLBACK (_device_list_count_cb), + &added_cnt); + g_signal_connect (device_list, "removed", + G_CALLBACK (_device_list_count_cb), + &removed_cnt); + g_signal_connect (device_list, "changed", + G_CALLBACK (_device_list_count_cb), + &changed_cnt); + + /* add child */ + fu_device_set_id (device_child, "child"); + fu_device_add_instance_id (device_child, "child-GUID-1"); + fu_device_convert_instance_ids (device_child); + fu_device_list_add (device_list, device_child); + g_assert_cmpint (added_cnt, ==, 1); + g_assert_cmpint (removed_cnt, ==, 0); + g_assert_cmpint (changed_cnt, ==, 0); + + /* add parent */ + fu_device_set_id (device_parent, "parent"); + fu_device_add_instance_id (device_parent, "parent-GUID-1"); + fu_device_convert_instance_ids (device_parent); + fu_device_add_child (device_parent, device_child); + fu_device_list_add (device_list, device_parent); + g_assert_cmpint (added_cnt, ==, 2); + g_assert_cmpint (removed_cnt, ==, 0); + g_assert_cmpint (changed_cnt, ==, 0); + + /* make sure that removing the parent causes both to go; but the child to go first */ + fu_device_list_remove (device_list, device_parent); + g_assert_cmpint (added_cnt, ==, 2); + g_assert_cmpint (removed_cnt, ==, 2); + g_assert_cmpint (changed_cnt, ==, 0); } static void @@ -881,10 +1738,12 @@ /* add both */ fu_device_set_id (device1, "device1"); - fu_device_add_guid (device1, "foobar"); + fu_device_add_instance_id (device1, "foobar"); + fu_device_convert_instance_ids (device1); fu_device_list_add (device_list, device1); fu_device_set_id (device2, "device2"); - fu_device_add_guid (device2, "baz"); + fu_device_add_instance_id (device2, "baz"); + fu_device_convert_instance_ids (device2); fu_device_list_add (device_list, device2); g_assert_cmpint (added_cnt, ==, 2); g_assert_cmpint (removed_cnt, ==, 0); @@ -905,6 +1764,7 @@ g_assert (device != NULL); g_assert_cmpstr (fu_device_get_id (device), ==, "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a"); + g_clear_object (&device); /* find by GUID */ device = fu_device_list_get_by_guid (device_list, @@ -914,6 +1774,7 @@ g_assert (device != NULL); g_assert_cmpstr (fu_device_get_id (device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); + g_clear_object (&device); /* find by missing GUID */ device = fu_device_list_get_by_guid (device_list, "notfound", &error); @@ -933,6 +1794,38 @@ "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); } +static void +fu_device_version_format_func (void) +{ + g_autoptr(FuDevice) device = fu_device_new (); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_ENSURE_SEMVER); + fu_device_set_version (device, "Ver1.2.3 RELEASE", FWUPD_VERSION_FORMAT_TRIPLET); + g_assert_cmpstr (fu_device_get_version (device), ==, "1.2.3"); +} + +static void +fu_device_open_refcount_func (void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(GError) error = NULL; + fu_device_set_id (device, "test_device"); + ret = fu_device_open (device, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_device_open (device, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_device_close (device, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_device_close (device, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_device_close (device, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); + g_assert_false (ret); +} static void fu_device_metadata_func (void) @@ -1140,23 +2033,6 @@ g_assert (device_tmp != NULL); g_assert_cmpstr (fu_device_get_id (device_tmp), ==, "b7eccd0059d6d7dc2ef76c35d6de0048cc8c029d"); g_clear_object (&device_tmp); - - /* add it with a small delay */ - fu_plugin_device_add_delay (plugin, device); - g_assert (device_tmp == NULL); - fu_test_loop_run_with_timeout (1000); - g_assert (device_tmp != NULL); - g_assert_cmpstr (fu_device_get_id (device_tmp), ==, "b7eccd0059d6d7dc2ef76c35d6de0048cc8c029d"); - g_clear_object (&device_tmp); - - /* add it again, twice quickly */ - fu_plugin_device_add_delay (plugin, device); - fu_plugin_device_add_delay (plugin, device); - g_assert (device_tmp == NULL); - fu_test_loop_run_with_timeout (1000); - g_assert (device_tmp != NULL); - g_assert_cmpstr (fu_device_get_id (device_tmp), ==, "b7eccd0059d6d7dc2ef76c35d6de0048cc8c029d"); - g_clear_object (&device_tmp); } static void @@ -1181,28 +2057,111 @@ fu_plugin_set_quirks (plugin, quirks); /* exact */ - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "USB\\VID_0A5C&PID_6412"); - g_assert_cmpstr (tmp, ==, "ignore-runtime"); - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "ACME Inc."); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "USB\\VID_0A5C&PID_6412", "Flags"); + g_assert_cmpstr (tmp, ==, "MERGE_ME,ignore-runtime"); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "ACME Inc.=True", "Test"); g_assert_cmpstr (tmp, ==, "awesome"); - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "CORP*"); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "CORP*", "Test"); g_assert_cmpstr (tmp, ==, "town"); - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "USB\\VID_FFFF&PID_FFFF"); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "USB\\VID_FFFF&PID_FFFF", "Flags"); g_assert_cmpstr (tmp, ==, ""); - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-Unfound", "baz"); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "baz", "Unfound"); g_assert_cmpstr (tmp, ==, NULL); - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-tests", "unfound"); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "unfound", "tests"); g_assert_cmpstr (tmp, ==, NULL); - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-unfound", "unfound"); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "unfound", "unfound"); g_assert_cmpstr (tmp, ==, NULL); + tmp = fu_plugin_lookup_quirk_by_id (plugin, "bb9ec3e2-77b3-53bc-a1f1-b05916715627", "Flags"); + g_assert_cmpstr (tmp, ==, "clever"); +} - /* glob */ - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "ACME*"); - g_assert_cmpstr (tmp, ==, "awesome"); - tmp = fu_quirks_lookup_by_glob (quirks, "fwupd-plugin-test", "CORPORATION"); - g_assert_cmpstr (tmp, ==, "town"); - tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "unfound*"); - g_assert_cmpstr (tmp, ==, NULL); +static void +fu_plugin_quirks_performance_func (void) +{ + g_autoptr(FuQuirks) quirks = fu_quirks_new (); + g_autoptr(GTimer) timer = g_timer_new (); + const gchar *keys[] = { + "Name", "Icon", "Children", "Plugin", "Flags", + "FirmwareSizeMin", "FirmwareSizeMax", NULL }; + + /* insert */ + for (guint j = 0; j < 1000; j++) { + g_autofree gchar *group = NULL; + group = g_strdup_printf ("DeviceInstanceId=USB\\VID_0BDA&PID_%04X", j); + for (guint i = 0; keys[i] != NULL; i++) + fu_quirks_add_value (quirks, group, keys[i], "Value"); + } + g_print ("insert=%.3fms ", g_timer_elapsed (timer, NULL) * 1000.f); + + /* lookup */ + g_timer_reset (timer); + for (guint j = 0; j < 1000; j++) { + g_autofree gchar *group = NULL; + group = g_strdup_printf ("DeviceInstanceId=USB\\VID_0BDA&PID_%04X", j); + for (guint i = 0; keys[i] != NULL; i++) { + const gchar *tmp = fu_quirks_lookup_by_id (quirks, group, keys[i]); + g_assert_cmpstr (tmp, ==, "Value"); + } + } + g_print ("lookup=%.3fms ", g_timer_elapsed (timer, NULL) * 1000.f); +} + +static void +fu_plugin_quirks_device_func (void) +{ + FuDevice *device_tmp; + GPtrArray *children; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuQuirks) quirks = fu_quirks_new (); + g_autoptr(GError) error = NULL; + + ret = fu_quirks_load (quirks, &error); + g_assert_no_error (error); + g_assert (ret); + + /* use quirk file to set device attributes */ + fu_device_set_physical_id (device, "usb:00:05"); + fu_device_set_quirks (device, quirks); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_instance_id (device, "USB\\VID_0BDA&PID_1100"); + fu_device_convert_instance_ids (device); + g_assert_cmpstr (fu_device_get_name (device), ==, "Hub"); + + /* ensure children are created */ + children = fu_device_get_children (device); + g_assert_cmpint (children->len, ==, 1); + device_tmp = g_ptr_array_index (children, 0); + g_assert_cmpstr (fu_device_get_name (device_tmp), ==, "HDMI"); + 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, FU_ENGINE_LOAD_FLAG_NONE, &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 @@ -1264,13 +2223,17 @@ g_assert_no_error (error); g_assert (mapped_file != NULL); blob_cab = g_mapped_file_get_bytes (mapped_file); - fwupd_release_set_version (fu_device_get_release_default (device), "1.2.3"); - ret = fu_plugin_runner_update (plugin, device, blob_cab, NULL, - FWUPD_INSTALL_FLAG_OFFLINE, &error); + release = fu_device_get_release_default (device); + fwupd_release_set_version (release, "1.2.3"); + ret = fu_plugin_runner_schedule_update (plugin, device, release, blob_cab, + FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (cnt, ==, 1); + /* set on the current device */ + g_assert_true (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)); + /* lets check the history */ history = fu_history_new (); device2 = fu_history_get_device_by_id (history, fu_device_get_id (device), &error); @@ -1278,6 +2241,7 @@ g_assert (device2 != NULL); g_assert_cmpint (fu_device_get_update_state (device2), ==, FWUPD_UPDATE_STATE_PENDING); g_assert_cmpstr (fu_device_get_update_error (device2), ==, NULL); + g_assert_true (fu_device_has_flag (device2, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)); release = fu_device_get_release_default (device2); g_assert (release != NULL); g_assert_cmpstr (fwupd_release_get_filename (release), !=, NULL); @@ -1287,7 +2251,7 @@ pending_cap = g_strdup (fwupd_release_get_filename (release)); /* lets do this online */ - ret = fu_plugin_runner_update (plugin, device, blob_cab, NULL, + ret = fu_plugin_runner_update (plugin, device, blob_cab, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error (error); g_assert (ret); @@ -1443,6 +2407,7 @@ FwupdRelease *release; g_autoptr(FuDevice) device_found = NULL; g_autoptr(FuHistory) history = NULL; + g_autoptr(GPtrArray) approved_firmware = NULL; g_autofree gchar *dirname = NULL; g_autofree gchar *filename = NULL; @@ -1461,7 +2426,7 @@ device = fu_device_new (); fu_device_set_id (device, "self-test"); fu_device_set_name (device, "ColorHug"), - fu_device_set_version (device, "3.0.1"), + fu_device_set_version (device, "3.0.1", FWUPD_VERSION_FORMAT_TRIPLET), fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); fu_device_set_update_error (device, "word"); fu_device_add_guid (device, "827edddd-9bb6-5632-889f-2c01255503da"); @@ -1532,6 +2497,23 @@ g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); g_assert (device_found == NULL); g_clear_error (&error); + + /* approved firmware */ + ret = fu_history_clear_approved_firmware (history, &error); + g_assert_no_error (error); + g_assert (ret); + ret = fu_history_add_approved_firmware (history, "foo", &error); + g_assert_no_error (error); + g_assert (ret); + ret = fu_history_add_approved_firmware (history, "bar", &error); + g_assert_no_error (error); + g_assert (ret); + approved_firmware = fu_history_get_approved_firmware (history, &error); + g_assert_no_error (error); + g_assert_nonnull (approved_firmware); + g_assert_cmpint (approved_firmware->len, ==, 2); + g_assert_cmpstr (g_ptr_array_index (approved_firmware, 0), ==, "foo"); + g_assert_cmpstr (g_ptr_array_index (approved_firmware, 1), ==, "bar"); } static void @@ -1579,7 +2561,9 @@ g_assert_no_error (error); g_assert_nonnull (blob_pass); blob_sig = g_bytes_new_static (sig_gpgme, strlen (sig_gpgme)); - result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, &error); + result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, + FU_KEYRING_VERIFY_FLAG_NONE, + &error); g_assert_no_error (error); g_assert_nonnull (result_pass); g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), == , 1438072952); @@ -1592,7 +2576,8 @@ blob_fail = fu_common_get_contents_bytes (fw_fail, &error); g_assert_no_error (error); g_assert_nonnull (blob_fail); - result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig, &error); + result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig, + FU_KEYRING_VERIFY_FLAG_NONE, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID); g_assert_null (result_fail); g_clear_error (&error); @@ -1642,7 +2627,8 @@ blob_sig = fu_common_get_contents_bytes (sig_fn, &error); g_assert_no_error (error); g_assert_nonnull (blob_sig); - result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, &error); + result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, + FU_KEYRING_VERIFY_FLAG_NONE, &error); g_assert_no_error (error); g_assert_nonnull (result_pass); g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), >= , 1502871248); @@ -1654,7 +2640,8 @@ blob_sig2 = fu_common_get_contents_bytes (sig_fn2, &error); g_assert_no_error (error); g_assert_nonnull (blob_sig2); - result_fail = fu_keyring_verify_data (keyring, blob_pass, blob_sig2, &error); + result_fail = fu_keyring_verify_data (keyring, blob_pass, blob_sig2, + FU_KEYRING_VERIFY_FLAG_NONE, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID); g_assert_null (result_fail); g_clear_error (&error); @@ -1665,7 +2652,8 @@ blob_fail = fu_common_get_contents_bytes (fw_fail, &error); g_assert_no_error (error); g_assert_nonnull (blob_fail); - result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig, &error); + result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig, + FU_KEYRING_VERIFY_FLAG_NONE, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID); g_assert_null (result_fail); g_clear_error (&error); @@ -1675,22 +2663,54 @@ } static void +fu_keyring_pkcs7_self_signed_func (void) +{ +#ifdef ENABLE_PKCS7 + gboolean ret; + g_autoptr(FuKeyring) kr = NULL; + g_autoptr(FuKeyringResult) kr_result = NULL; + g_autoptr(GBytes) payload = NULL; + g_autoptr(GBytes) signature = NULL; + g_autoptr(GError) error = NULL; + +#ifndef HAVE_GNUTLS_3_6_0 + /* required to create the private key correctly */ + g_test_skip ("GnuTLS version too old"); + return; +#endif + + /* create detached signature and verify */ + kr = fu_keyring_pkcs7_new (); + ret = fu_keyring_setup (kr, &error); + g_assert_no_error (error); + g_assert_true (ret); + payload = fu_common_get_contents_bytes ("/etc/machine-id", &error); + g_assert_no_error (error); + g_assert_nonnull (payload); + signature = fu_keyring_sign_data (kr, payload, FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP, &error); + g_assert_no_error (error); + g_assert_nonnull (signature); + ret = fu_common_set_contents_bytes ("/tmp/test.p7b", signature, &error); + g_assert_no_error (error); + g_assert_true (ret); + kr_result = fu_keyring_verify_data (kr, payload, signature, + FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT, &error); + g_assert_no_error (error); + g_assert_nonnull (kr_result); +#else + g_test_skip ("no GnuTLS support enabled"); +#endif +} + +static void fu_common_firmware_builder_func (void) { const gchar *data; g_autofree gchar *archive_fn = NULL; - g_autofree gchar *bwrap_fn = NULL; g_autoptr(GBytes) archive_blob = NULL; g_autoptr(GBytes) firmware_blob = NULL; g_autoptr(GError) error = NULL; - /* we can't do this in travis: capset failed: Operation not permitted */ - bwrap_fn = g_find_program_in_path ("bwrap"); - if (bwrap_fn == NULL) { - g_test_skip ("no bwrap in path, so skipping"); - return; - } - /* get test file */ archive_fn = fu_test_get_filename (TESTDATADIR, "builder/firmware.tar"); g_assert (archive_fn != NULL); @@ -1703,8 +2723,17 @@ "startup.sh", "firmware.bin", &error); - g_assert_no_error (error); - g_assert (firmware_blob != NULL); + if (firmware_blob == NULL) { + if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_PERMISSION_DENIED)) { + g_test_skip ("Missing permissions to create namespace in container"); + return; + } + if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip ("User namespaces not supported in container"); + return; + } + g_assert_no_error (error); + } /* check it */ data = g_bytes_get_data (firmware_blob, NULL); @@ -1788,13 +2817,31 @@ g_assert (fn != NULL); argv[0] = fn; ret = fu_common_spawn_sync (argv, - fu_test_stdout_cb, &lines, NULL, &error); + fu_test_stdout_cb, &lines, 0, NULL, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (lines, ==, 6); } static void +fu_common_spawn_timeout_func (void) +{ + gboolean ret; + guint lines = 0; + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = NULL; + const gchar *argv[3] = { "replace", "test", NULL }; + + fn = fu_test_get_filename (TESTDATADIR, "spawn.sh"); + g_assert (fn != NULL); + argv[0] = fn; + ret = fu_common_spawn_sync (argv, fu_test_stdout_cb, &lines, 50, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert (!ret); + g_assert_cmpint (lines, ==, 1); +} + +static void fu_progressbar_func (void) { g_autoptr(FuProgressbar) progressbar = fu_progressbar_new (); @@ -1899,18 +2946,192 @@ } static void +_plugin_composite_device_added_cb (FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + GPtrArray *devices = (GPtrArray *) user_data; + g_ptr_array_add (devices, g_object_ref (device)); +} + +static void +fu_plugin_composite_func (void) +{ + GError *error = NULL; + gboolean ret; + g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); + g_autoptr(FuPlugin) plugin = fu_plugin_new (); + 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(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, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " \n" + "", + "acme.module1.metainfo.xml", + "\n" + " com.acme.example.firmware.module1\n" + " \n" + " 7fddead7-12b5-4fb9-9fa0-6d30305df755\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " plain\n" + " \n" + "", + "acme.module2.metainfo.xml", + "\n" + " com.acme.example.firmware.module2\n" + " \n" + " b8fe6b45-8702-4bcd-8120-ef236caac76f\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " plain\n" + " \n" + "", + "firmware.bin", "world", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + silo = fu_common_cab_build_silo (blob, 10240, &error); + g_assert_no_error (error); + 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); + ret = fu_plugin_open (plugin, PLUGINBUILDDIR "/libfu_plugin_test.so", &error); + g_assert_no_error (error); + g_assert_true (ret); + fu_engine_add_plugin (engine, plugin); + + ret = fu_plugin_runner_startup (plugin, &error); + g_assert_no_error (error); + g_assert_true (ret); + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_signal_connect (plugin, "device-added", + G_CALLBACK (_plugin_composite_device_added_cb), + devices); + + ret = fu_plugin_runner_coldplug (plugin, &error); + g_assert_no_error (error); + g_assert_true (ret); + + /* check we found all composite devices */ + g_assert_cmpint (devices->len, ==, 3); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + fu_engine_add_device (engine, device); + if (g_strcmp0 (fu_device_get_id (device), + "08d460be0f1f9f128413f816022a6439e0078018") == 0) { + g_assert_cmpstr (fu_device_get_version (device), ==, "1.2.2"); + } else if (g_strcmp0 (fu_device_get_id (device), + "c0a0a4aa6480ac28eea1ce164fbb466ca934e1ff") == 0) { + g_assert_cmpstr (fu_device_get_version (device), ==, "1"); + g_assert_nonnull (fu_device_get_parent (device)); + } else if (g_strcmp0 (fu_device_get_id (device), + "bf455e9f371d2608d1cb67660fd2b335d3f6ef73") == 0) { + g_assert_cmpstr (fu_device_get_version (device), ==, "10"); + g_assert_nonnull (fu_device_get_parent (device)); + } + } + + /* produce install tasks */ + 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++) { + FuDevice *device = g_ptr_array_index (devices, j); + g_autoptr(FuInstallTask) task = NULL; + g_autoptr(GError) error_local = NULL; + + /* is this component valid for the device */ + 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), + xb_node_query_text (component, "id", NULL), + error_local->message); + continue; + } + + g_ptr_array_add (install_tasks, g_steal_pointer (&task)); + } + } + g_assert_cmpint (install_tasks->len, ==, 3); + + /* install the cab */ + ret = fu_engine_install_tasks (engine, + install_tasks, + blob, + FWUPD_DEVICE_FLAG_NONE, + &error); + g_assert_no_error (error); + g_assert_true (ret); + + /* verify everything upgraded */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + const gchar *metadata; + if (g_strcmp0 (fu_device_get_id (device), + "08d460be0f1f9f128413f816022a6439e0078018") == 0) { + g_assert_cmpstr (fu_device_get_version (device), ==, "1.2.3"); + } else if (g_strcmp0 (fu_device_get_id (device), + "c0a0a4aa6480ac28eea1ce164fbb466ca934e1ff") == 0) { + g_assert_cmpstr (fu_device_get_version (device), ==, "2"); + } else if (g_strcmp0 (fu_device_get_id (device), + "bf455e9f371d2608d1cb67660fd2b335d3f6ef73") == 0) { + g_assert_cmpstr (fu_device_get_version (device), ==, "11"); + } + + /* verify prepare and cleanup ran on all devices */ + metadata = fu_device_get_metadata (device, "frimbulator"); + g_assert_cmpstr (metadata, ==, "1"); + metadata = fu_device_get_metadata (device, "frombulator"); + g_assert_cmpstr (metadata, ==, "1"); + } +} + +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" @@ -1921,9 +3142,8 @@ " \n" " \n" " \n" - " \n" " 5\n" - " 7c211433f02071597741e6ff5a8ea34789abbf43\n" + " 7c211433f02071597741e6ff5a8ea34789abbf43\n" "

We fixed things

\n" "
\n" "
\n" @@ -1938,38 +3158,42 @@ 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" @@ -1984,36 +3208,37 @@ 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" @@ -2028,26 +3253,26 @@ 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; @@ -2059,15 +3284,15 @@ 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; @@ -2088,15 +3313,15 @@ 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; @@ -2116,15 +3341,15 @@ 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; @@ -2142,15 +3367,15 @@ 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; @@ -2170,9 +3395,273 @@ 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 +fu_device_poll_cb (FuDevice *device, GError **error) +{ + guint64 cnt = fu_device_get_metadata_integer (device, "cnt"); + g_debug ("poll cnt=%" G_GUINT64_FORMAT, cnt); + fu_device_set_metadata_integer (device, "cnt", cnt + 1); + return TRUE; +} + +static void +fu_device_poll_func (void) +{ + g_autoptr(FuDevice) device = fu_device_new (); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); + guint cnt; + + /* set up a 10ms poll */ + klass->poll = fu_device_poll_cb; + fu_device_set_metadata_integer (device, "cnt", 0); + fu_device_set_poll_interval (device, 10); + fu_test_loop_run_with_timeout (100); + fu_test_loop_quit (); + cnt = fu_device_get_metadata_integer (device, "cnt"); + g_assert_cmpint (cnt, >=, 8); + + /* disable the poll */ + fu_device_set_poll_interval (device, 0); + fu_test_loop_run_with_timeout (100); + fu_test_loop_quit (); + g_assert_cmpint (fu_device_get_metadata_integer (device, "cnt"), ==, cnt); +} + +static void +fu_device_incorporate_func (void) +{ + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuDevice) donor = fu_device_new (); + + /* set up donor device */ + fu_device_set_alternate_id (donor, "alt-id"); + fu_device_set_equivalent_id (donor, "equiv-id"); + fu_device_set_metadata (donor, "test", "me"); + fu_device_set_metadata (donor, "test2", "me"); + + /* base properties */ + fu_device_add_flag (donor, FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_set_created (donor, 123); + fu_device_set_modified (donor, 456); + fu_device_add_icon (donor, "computer"); + + /* existing properties */ + fu_device_set_equivalent_id (device, "DO_NOT_OVERWRITE"); + fu_device_set_metadata (device, "test2", "DO_NOT_OVERWRITE"); + fu_device_set_modified (device, 789); + + /* incorporate properties from donor to device */ + fu_device_incorporate (device, donor); + g_assert_cmpstr (fu_device_get_alternate_id (device), ==, "alt-id"); + g_assert_cmpstr (fu_device_get_equivalent_id (device), ==, "DO_NOT_OVERWRITE"); + g_assert_cmpstr (fu_device_get_metadata (device, "test"), ==, "me"); + g_assert_cmpstr (fu_device_get_metadata (device, "test2"), ==, "DO_NOT_OVERWRITE"); + g_assert_true (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC)); + g_assert_cmpint (fu_device_get_created (device), ==, 123); + g_assert_cmpint (fu_device_get_modified (device), ==, 789); + g_assert_cmpint (fu_device_get_icons(device)->len, ==, 1); +} + +static void +fu_chunk_func (void) +{ + g_autofree gchar *chunked1_str = NULL; + g_autofree gchar *chunked2_str = NULL; + g_autofree gchar *chunked3_str = NULL; + g_autofree gchar *chunked4_str = NULL; + g_autoptr(GPtrArray) chunked1 = NULL; + g_autoptr(GPtrArray) chunked2 = NULL; + g_autoptr(GPtrArray) chunked3 = NULL; + g_autoptr(GPtrArray) chunked4 = NULL; + + chunked3 = fu_chunk_array_new ((const guint8 *) "123456", 6, 0x0, 3, 3); + chunked3_str = fu_chunk_array_to_string (chunked3); + g_print ("\n%s", chunked3_str); + g_assert_cmpstr (chunked3_str, ==, "#00: page:00 addr:0000 len:03 123\n" + "#01: page:01 addr:0000 len:03 456\n"); + + chunked4 = fu_chunk_array_new ((const guint8 *) "123456", 6, 0x4, 4, 4); + chunked4_str = fu_chunk_array_to_string (chunked4); + g_print ("\n%s", chunked4_str); + g_assert_cmpstr (chunked4_str, ==, "#00: page:01 addr:0000 len:04 1234\n" + "#01: page:02 addr:0000 len:02 56\n"); + + chunked1 = fu_chunk_array_new ((const guint8 *) "0123456789abcdef", 16, 0x0, 10, 4); + chunked1_str = fu_chunk_array_to_string (chunked1); + g_print ("\n%s", chunked1_str); + g_assert_cmpstr (chunked1_str, ==, "#00: page:00 addr:0000 len:04 0123\n" + "#01: page:00 addr:0004 len:04 4567\n" + "#02: page:00 addr:0008 len:02 89\n" + "#03: page:01 addr:0000 len:04 abcd\n" + "#04: page:01 addr:0004 len:02 ef\n"); + + chunked2 = fu_chunk_array_new ((const guint8 *) "XXXXXXYYYYYYZZZZZZ", 18, 0x0, 6, 4); + chunked2_str = fu_chunk_array_to_string (chunked2); + g_print ("\n%s", chunked2_str); + g_assert_cmpstr (chunked2_str, ==, "#00: page:00 addr:0000 len:04 XXXX\n" + "#01: page:00 addr:0004 len:02 XX\n" + "#02: page:01 addr:0000 len:04 YYYY\n" + "#03: page:01 addr:0004 len:02 YY\n" + "#04: page:02 addr:0000 len:04 ZZZZ\n" + "#05: page:02 addr:0004 len:02 ZZ\n"); +} + +static void +fu_common_strstrip_func (void) +{ + + struct { + const gchar *old; + const gchar *new; + } map[] = { + { "same", "same" }, + { " leading", "leading" }, + { "tailing ", "tailing" }, + { " b ", "b" }, + { " ", "" }, + { NULL, NULL } + }; + for (guint i = 0; map[i].old != NULL; i++) { + g_autofree gchar *tmp = fu_common_strstrip (map[i].old); + g_assert_cmpstr (tmp, ==, map[i].new); + } +} + +static void +fu_common_version_func (void) +{ + guint i; + struct { + guint32 val; + const gchar *ver; + FwupdVersionFormat flags; + } version_from_uint32[] = { + { 0x0, "0.0.0.0", FWUPD_VERSION_FORMAT_QUAD }, + { 0xff, "0.0.0.255", FWUPD_VERSION_FORMAT_QUAD }, + { 0xff01, "0.0.255.1", FWUPD_VERSION_FORMAT_QUAD }, + { 0xff0001, "0.255.0.1", FWUPD_VERSION_FORMAT_QUAD }, + { 0xff000100, "255.0.1.0", FWUPD_VERSION_FORMAT_QUAD }, + { 0x0, "0.0.0", FWUPD_VERSION_FORMAT_TRIPLET }, + { 0xff, "0.0.255", FWUPD_VERSION_FORMAT_TRIPLET }, + { 0xff01, "0.0.65281", FWUPD_VERSION_FORMAT_TRIPLET }, + { 0xff0001, "0.255.1", FWUPD_VERSION_FORMAT_TRIPLET }, + { 0xff000100, "255.0.256", FWUPD_VERSION_FORMAT_TRIPLET }, + { 0x0, "0", FWUPD_VERSION_FORMAT_NUMBER }, + { 0xff000100, "4278190336", FWUPD_VERSION_FORMAT_NUMBER }, + { 0x0, "11.0.0.0", FWUPD_VERSION_FORMAT_INTEL_ME }, + { 0xffffffff, "18.31.255.65535", FWUPD_VERSION_FORMAT_INTEL_ME }, + { 0x0b32057a, "11.11.50.1402", FWUPD_VERSION_FORMAT_INTEL_ME }, + { 0xb8320d84, "11.8.50.3460", FWUPD_VERSION_FORMAT_INTEL_ME2 }, + { 0, NULL } + }; + struct { + guint16 val; + const gchar *ver; + FwupdVersionFormat flags; + } version_from_uint16[] = { + { 0x0, "0.0", FWUPD_VERSION_FORMAT_PAIR }, + { 0xff, "0.255", FWUPD_VERSION_FORMAT_PAIR }, + { 0xff01, "255.1", FWUPD_VERSION_FORMAT_PAIR }, + { 0x0, "0.0", FWUPD_VERSION_FORMAT_BCD }, + { 0x0110, "1.10", FWUPD_VERSION_FORMAT_BCD }, + { 0x9999, "99.99", FWUPD_VERSION_FORMAT_BCD }, + { 0x0, "0", FWUPD_VERSION_FORMAT_NUMBER }, + { 0x1234, "4660", FWUPD_VERSION_FORMAT_NUMBER }, + { 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 @@ -2183,29 +3672,53 @@ /* only critical and error are fatal */ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); + g_setenv ("FWUPD_DATADIR", TESTDATADIR_SRC, TRUE); + g_setenv ("FWUPD_PLUGINDIR", TESTDATADIR_SRC, TRUE); + g_setenv ("FWUPD_SYSCONFDIR", TESTDATADIR_SRC, TRUE); + 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); g_test_add_func ("/fwupd/device-locker{success}", fu_device_locker_func); 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); + g_test_add_func ("/fwupd/device-list{remove-chain}", fu_device_list_remove_chain_func); + g_test_add_func ("/fwupd/engine{device-unlock}", fu_engine_device_unlock_func); g_test_add_func ("/fwupd/engine{history-success}", fu_engine_history_func); g_test_add_func ("/fwupd/engine{history-error}", fu_engine_history_error_func); + if (g_test_slow ()) + g_test_add_func ("/fwupd/device-list{replug-auto}", fu_device_list_replug_auto_func); + g_test_add_func ("/fwupd/device-list{replug-user}", fu_device_list_replug_user_func); g_test_add_func ("/fwupd/engine{require-hwid}", fu_engine_require_hwid_func); + g_test_add_func ("/fwupd/engine{history-inherit}", fu_engine_history_inherit); g_test_add_func ("/fwupd/engine{partial-hash}", fu_engine_partial_hash_func); g_test_add_func ("/fwupd/engine{downgrade}", fu_engine_downgrade_func); g_test_add_func ("/fwupd/engine{requirements-success}", fu_engine_requirements_func); g_test_add_func ("/fwupd/engine{requirements-missing}", fu_engine_requirements_missing_func); + g_test_add_func ("/fwupd/engine{requirements-not-child}", fu_engine_requirements_child_func); + g_test_add_func ("/fwupd/engine{requirements-not-child-fail}", fu_engine_requirements_child_fail_func); g_test_add_func ("/fwupd/engine{requirements-unsupported}", fu_engine_requirements_unsupported_func); g_test_add_func ("/fwupd/engine{requirements-device}", fu_engine_requirements_device_func); + g_test_add_func ("/fwupd/engine{requirements-version-format}", fu_engine_requirements_version_format_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); @@ -2216,8 +3729,18 @@ g_test_add_func ("/fwupd/plugin{delay}", fu_plugin_delay_func); g_test_add_func ("/fwupd/plugin{module}", fu_plugin_module_func); g_test_add_func ("/fwupd/plugin{quirks}", fu_plugin_quirks_func); + g_test_add_func ("/fwupd/plugin{quirks-performance}", fu_plugin_quirks_performance_func); + g_test_add_func ("/fwupd/plugin{quirks-device}", fu_plugin_quirks_device_func); + 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/keyring{pkcs7-self-signed}", fu_keyring_pkcs7_self_signed_func); + g_test_add_func ("/fwupd/plugin{build-hash}", fu_plugin_hash_func); + g_test_add_func ("/fwupd/chunk", fu_chunk_func); + g_test_add_func ("/fwupd/common{version-guess-format}", fu_common_version_guess_format_func); + g_test_add_func ("/fwupd/common{version}", fu_common_version_func); + g_test_add_func ("/fwupd/common{vercmp}", fu_common_vercmp_func); + 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); g_test_add_func ("/fwupd/common{cab-success-unsigned}", fu_common_store_cab_unsigned_func); @@ -2228,6 +3751,7 @@ g_test_add_func ("/fwupd/common{cab-error-missing-file}", fu_common_store_cab_error_missing_file_func); g_test_add_func ("/fwupd/common{cab-error-size}", fu_common_store_cab_error_size_func); g_test_add_func ("/fwupd/common{spawn)", fu_common_spawn_func); + g_test_add_func ("/fwupd/common{spawn-timeout)", fu_common_spawn_timeout_func); g_test_add_func ("/fwupd/common{firmware-builder}", fu_common_firmware_builder_func); return g_test_run (); } diff -Nru fwupd-1.0.9/src/fu-smbios.c fwupd-1.2.10/src/fu-smbios.c --- fwupd-1.0.9/src/fu-smbios.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-smbios.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,17 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuSmbios" + #include "config.h" #include #include +#include "fu-common.h" #include "fu-smbios.h" #include "fwupd-error.h" @@ -311,8 +313,10 @@ fu_smbios_setup (FuSmbios *self, GError **error) { g_autofree gchar *path = NULL; + g_autofree gchar *sysfsfwdir = NULL; g_return_val_if_fail (FU_IS_SMBIOS (self), FALSE); - path = g_build_filename (SYSFSFIRMWAREDIR, "dmi", "tables", NULL); + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + path = g_build_filename (sysfsfwdir, "dmi", "tables", NULL); return fu_smbios_setup_from_path (self, path, error); } diff -Nru fwupd-1.0.9/src/fu-smbios.h fwupd-1.2.10/src/fu-smbios.h --- fwupd-1.0.9/src/fu-smbios.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-smbios.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_SMBIOS_H -#define __FU_SMBIOS_H +#pragma once #include @@ -42,5 +40,3 @@ GError **error); G_END_DECLS - -#endif /* __FU_SMBIOS_H */ diff -Nru fwupd-1.0.9/src/fu-systemd.c fwupd-1.2.10/src/fu-systemd.c --- fwupd-1.0.9/src/fu-systemd.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-systemd.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include + +#include +#include +#include + +#include "fu-systemd.h" + +#define SYSTEMD_SERVICE "org.freedesktop.systemd1" +#define SYSTEMD_OBJECT_PATH "/org/freedesktop/systemd1" +#define SYSTEMD_INTERFACE "org.freedesktop.systemd1" +#define SYSTEMD_MANAGER_INTERFACE "org.freedesktop.systemd1.Manager" + +static GDBusProxy * +fu_systemd_get_manager (GError **error) +{ + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GDBusProxy) proxy = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) { + g_prefix_error (error, "failed to get bus: "); + return NULL; + } + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, NULL, + SYSTEMD_SERVICE, + SYSTEMD_OBJECT_PATH, + SYSTEMD_MANAGER_INTERFACE, + NULL, error); + if (proxy == NULL) { + g_prefix_error (error, "failed to find %s: ", SYSTEMD_SERVICE); + return NULL; + } + return g_steal_pointer (&proxy); +} + +static gchar * +fu_systemd_unit_get_path (GDBusProxy *proxy_manager, const gchar *unit, GError **error) +{ + g_autofree gchar *path = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_sync (proxy_manager, + "GetUnit", + g_variant_new ("(s)", unit), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, error); + if (val == NULL) { + g_prefix_error (error, "failed to find %s: ", unit); + return NULL; + } + g_variant_get (val, "(o)", &path); + return g_steal_pointer (&path); +} + +static GDBusProxy * +fu_systemd_unit_get_proxy (GDBusProxy *proxy_manager, const gchar *unit, GError **error) +{ + g_autofree gchar *path = NULL; + g_autoptr(GDBusProxy) proxy_unit = NULL; + + path = fu_systemd_unit_get_path (proxy_manager, unit, error); + if (path == NULL) + return NULL; + proxy_unit = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_manager), + G_DBUS_PROXY_FLAGS_NONE, NULL, + SYSTEMD_SERVICE, + path, + SYSTEMD_INTERFACE, + NULL, error); + if (proxy_unit == NULL) { + g_prefix_error (error, "failed to register proxy for %s: ", path); + return NULL; + } + return g_steal_pointer (&proxy_unit); +} + +gchar * +fu_systemd_get_default_target (GError **error) +{ + const gchar *path = NULL; + g_autoptr(GDBusProxy) proxy_manager = NULL; + g_autoptr(GVariant) val = NULL; + + proxy_manager = fu_systemd_get_manager (error); + if (proxy_manager == NULL) + return NULL; + val = g_dbus_proxy_call_sync (proxy_manager, + "GetDefaultTarget", NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, error); + if (val == NULL) + return NULL; + g_variant_get (val, "(&s)", &path); + return g_strdup (path); + +} + +gboolean +fu_systemd_unit_stop (const gchar *unit, GError **error) +{ + g_autoptr(GDBusProxy) proxy_manager = NULL; + g_autoptr(GDBusProxy) proxy_unit = NULL; + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail (unit != NULL, FALSE); + + proxy_manager = fu_systemd_get_manager (error); + if (proxy_manager == NULL) + return FALSE; + proxy_unit = fu_systemd_unit_get_proxy (proxy_manager, unit, error); + if (proxy_unit == NULL) + return FALSE; + val = g_dbus_proxy_call_sync (proxy_unit, + "StopUnit", + g_variant_new ("(ss)", unit, "replace"), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, error); + return val != NULL; +} + +gboolean +fu_systemd_unit_enable (const gchar *unit, GError **error) +{ + const gchar *units[] = { unit, NULL }; + g_autoptr(GDBusProxy) proxy_manager = NULL; + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail (unit != NULL, FALSE); + + proxy_manager = fu_systemd_get_manager (error); + if (proxy_manager == NULL) + return FALSE; + val = g_dbus_proxy_call_sync (proxy_manager, + "EnableUnitFiles", + g_variant_new ("(^asbb)", units, TRUE, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, error); + return val != NULL; +} + +gboolean +fu_systemd_unit_disable (const gchar *unit, GError **error) +{ + const gchar *units[] = { unit, NULL }; + g_autoptr(GDBusProxy) proxy_manager = NULL; + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail (unit != NULL, FALSE); + + proxy_manager = fu_systemd_get_manager (error); + if (proxy_manager == NULL) + return FALSE; + val = g_dbus_proxy_call_sync (proxy_manager, + "DisableUnitFiles", + g_variant_new ("(^asb)", units, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, error); + return val != NULL; +} + +gboolean +fu_systemd_unit_check_exists (const gchar *unit, GError **error) +{ + g_autoptr(GDBusProxy) proxy_manager = NULL; + g_autofree gchar *path = NULL; + + g_return_val_if_fail (unit != NULL, FALSE); + + proxy_manager = fu_systemd_get_manager (error); + if (proxy_manager == NULL) + return FALSE; + path = fu_systemd_unit_get_path (proxy_manager, unit, error); + return path != NULL; +} diff -Nru fwupd-1.0.9/src/fu-systemd.h fwupd-1.2.10/src/fu-systemd.h --- fwupd-1.0.9/src/fu-systemd.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-systemd.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +gboolean fu_systemd_unit_check_exists (const gchar *unit, + GError **error); +gboolean fu_systemd_unit_stop (const gchar *unit, + GError **error); +gboolean fu_systemd_unit_enable (const gchar *unit, + GError **error); +gboolean fu_systemd_unit_disable (const gchar *unit, + GError **error); +gchar *fu_systemd_get_default_target (GError **error); + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-test.c fwupd-1.2.10/src/fu-test.c --- fwupd-1.0.9/src/fu-test.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-test.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2010-2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ diff -Nru fwupd-1.0.9/src/fu-test.h fwupd-1.2.10/src/fu-test.h --- fwupd-1.0.9/src/fu-test.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-test.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2010-2011 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_TEST_H__ -#define __FU_TEST_H__ +#pragma once #include +G_BEGIN_DECLS + gchar *fu_test_get_filename (const gchar *testdatadirs, const gchar *filename); void fu_test_loop_run_with_timeout (guint timeout_ms); @@ -18,4 +18,4 @@ const gchar *txt2, GError **error); -#endif /* __FU_TEST_H__ */ +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-tool.c fwupd-1.2.10/src/fu-tool.c --- fwupd-1.0.9/src/fu-tool.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-tool.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,66 +1,129 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuMain" + #include "config.h" #include +#include #include #include #include #include #include +#include +#include "fu-device-private.h" #include "fu-engine.h" +#include "fu-history.h" #include "fu-plugin-private.h" #include "fu-progressbar.h" #include "fu-smbios.h" #include "fu-util-common.h" - -/* this is only valid in this file */ -#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1) +#include "fu-debug.h" +#include "fwupd-common-private.h" +#include "fwupd-device-private.h" + +#ifdef HAVE_SYSTEMD +#include "fu-systemd.h" +#endif /* custom return code */ #define EXIT_NOTHING_TO_DO 2 -typedef struct { +typedef enum { + FU_UTIL_OPERATION_UNKNOWN, + FU_UTIL_OPERATION_UPDATE, + FU_UTIL_OPERATION_INSTALL, + FU_UTIL_OPERATION_LAST +} FuUtilOperation; + +struct FuUtilPrivate { GCancellable *cancellable; GMainLoop *loop; GOptionContext *context; - GPtrArray *cmd_array; FuEngine *engine; FuProgressbar *progressbar; + gboolean no_reboot_check; + gboolean prepare_blob; + gboolean cleanup_blob; + gboolean enable_json_state; FwupdInstallFlags flags; gboolean show_all_devices; -} FuUtilPrivate; + /* only valid in update and downgrade */ + FuUtilOperation current_operation; + FwupdDevice *current_device; + gchar *current_message; + FwupdDeviceFlags completion_flags; +}; + +static gboolean +fu_util_save_current_state (FuUtilPrivate *priv, GError **error) +{ + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autofree gchar *state = NULL; + g_autofree gchar *dirname = NULL; + g_autofree gchar *filename = NULL; + + if (!priv->enable_json_state) + return TRUE; -typedef gboolean (*FuUtilPrivateCb) (FuUtilPrivate *util, - gchar **values, - GError **error); - -typedef struct { - gchar *name; - gchar *arguments; - gchar *description; - FuUtilPrivateCb callback; -} FuUtilItem; - -static void -fu_util_item_free (FuUtilItem *item) -{ - g_free (item->name); - g_free (item->arguments); - g_free (item->description); - g_free (item); + devices = fu_engine_get_devices (priv->engine, error); + if (devices == NULL) + return FALSE; + + /* create header */ + builder = json_builder_new (); + json_builder_begin_object (builder); + + /* add each device */ + json_builder_set_member_name (builder, "Devices"); + json_builder_begin_array (builder); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devices, i); + json_builder_begin_object (builder); + fwupd_device_to_json (dev, builder); + json_builder_end_object (builder); + } + json_builder_end_array (builder); + json_builder_end_object (builder); + + /* export as a string */ + json_root = json_builder_get_root (builder); + json_generator = json_generator_new (); + json_generator_set_pretty (json_generator, TRUE); + json_generator_set_root (json_generator, json_root); + state = json_generator_to_data (json_generator, NULL); + if (state == NULL) + return FALSE; + dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + filename = g_build_filename (dirname, "state.json", NULL); + return g_file_set_contents (filename, state, -1, error); } -static gint -fu_sort_command_name_cb (FuUtilItem **item1, FuUtilItem **item2) +static gboolean +fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error) { - return g_strcmp0 ((*item1)->name, (*item2)->name); + g_autoptr(GError) error_local = NULL; + +#ifdef HAVE_SYSTEMD + if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local)) + g_debug ("Failed top stop daemon: %s", error_local->message); +#endif + if (!fu_engine_load (priv->engine, flags, 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 void @@ -75,96 +138,6 @@ } static void -fu_util_add (GPtrArray *array, - const gchar *name, - const gchar *arguments, - const gchar *description, - FuUtilPrivateCb callback) -{ - g_auto(GStrv) names = NULL; - - g_return_if_fail (name != NULL); - g_return_if_fail (description != NULL); - g_return_if_fail (callback != NULL); - - /* add each one */ - names = g_strsplit (name, ",", -1); - for (guint i = 0; names[i] != NULL; i++) { - FuUtilItem *item = g_new0 (FuUtilItem, 1); - item->name = g_strdup (names[i]); - if (i == 0) { - item->description = g_strdup (description); - } else { - /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */ - item->description = g_strdup_printf (_("Alias to %s"), - names[0]); - } - item->arguments = g_strdup (arguments); - item->callback = callback; - g_ptr_array_add (array, item); - } -} - -static gchar * -fu_util_get_descriptions (GPtrArray *array) -{ - gsize len; - const gsize max_len = 35; - GString *string; - - /* print each command */ - string = g_string_new (""); - for (guint i = 0; i < array->len; i++) { - FuUtilItem *item = g_ptr_array_index (array, i); - g_string_append (string, " "); - g_string_append (string, item->name); - len = strlen (item->name) + 2; - if (item->arguments != NULL) { - g_string_append (string, " "); - g_string_append (string, item->arguments); - len += strlen (item->arguments) + 1; - } - if (len < max_len) { - for (gsize j = len; j < max_len + 1; j++) - g_string_append_c (string, ' '); - g_string_append (string, item->description); - g_string_append_c (string, '\n'); - } else { - g_string_append_c (string, '\n'); - for (gsize j = 0; j < max_len + 1; j++) - g_string_append_c (string, ' '); - g_string_append (string, item->description); - g_string_append_c (string, '\n'); - } - } - - /* remove trailing newline */ - if (string->len > 0) - g_string_set_size (string, string->len - 1); - - return g_string_free (string, FALSE); -} - -static gboolean -fu_util_run (FuUtilPrivate *priv, const gchar *command, gchar **values, GError **error) -{ - /* find command */ - for (guint i = 0; i < priv->cmd_array->len; i++) { - FuUtilItem *item = g_ptr_array_index (priv->cmd_array, i); - if (g_strcmp0 (item->name, command) == 0) - return item->callback (priv, values, error); - } - - /* not found */ - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - /* TRANSLATORS: error message */ - _("Command not found")); - return FALSE; -} - -static void fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data) { FuUtilPrivate *priv = (FuUtilPrivate *) user_data; @@ -193,12 +166,6 @@ return TRUE; } -static void -fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level, - const gchar *message, gpointer user_data) -{ -} - static gboolean fu_util_sigint_cb (gpointer user_data) { @@ -211,8 +178,8 @@ static void fu_util_private_free (FuUtilPrivate *priv) { - if (priv->cmd_array != NULL) - g_ptr_array_unref (priv->cmd_array); + if (priv->current_device != NULL) + g_object_unref (priv->current_device); if (priv->engine != NULL) g_object_unref (priv->engine); if (priv->loop != NULL) @@ -223,6 +190,7 @@ g_object_unref (priv->progressbar); if (priv->context != NULL) g_option_context_free (priv->context); + g_free (priv->current_message); g_free (priv); } @@ -251,15 +219,6 @@ } static void -fu_main_engine_device_changed_cb (FuEngine *engine, - FuDevice *device, - FuUtilPrivate *priv) -{ - g_autofree gchar *tmp = fu_device_to_string (device); - g_debug ("CHANGED:\n%s", tmp); -} - -static void fu_main_engine_status_changed_cb (FuEngine *engine, FwupdStatus status, FuUtilPrivate *priv) @@ -278,7 +237,7 @@ static gboolean fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error) { - if (!fu_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; g_main_loop_run (priv->loop); return TRUE; @@ -320,13 +279,60 @@ } 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, FU_ENGINE_LOAD_FLAG_NONE, 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)); + } + } + + /* save the device state for other applications to see */ + if (!fu_util_save_current_state (priv, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) array = NULL; gint fd; /* load engine */ - if (!fu_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* check args */ @@ -369,7 +375,7 @@ g_autoptr(GPtrArray) devs = NULL; /* load engine */ - if (!fu_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* print */ @@ -382,14 +388,17 @@ return TRUE; } for (guint i = 0; i < devs->len; i++) { - g_autofree gchar *tmp = NULL; FwupdDevice *dev = g_ptr_array_index (devs, i); - if (!(fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE) || priv->show_all_devices)) - continue; - tmp = fwupd_device_to_string (dev); - g_print ("%s\n", tmp); + if (priv->show_all_devices || fu_util_is_interesting_device (dev)) { + g_autofree gchar *tmp = fwupd_device_to_string (dev); + g_print ("%s\n", tmp); + } } + /* save the device state for other applications to see */ + if (!fu_util_save_current_state (priv, error)) + return FALSE; + return TRUE; } @@ -398,7 +407,8 @@ { for (guint i = 0; i < devs->len; i++) { FuDevice *dev_tmp = g_ptr_array_index (devs, i); - if (!(fu_device_has_flag (dev_tmp, FWUPD_DEVICE_FLAG_UPDATABLE) || priv->show_all_devices)) + if (!priv->show_all_devices && + !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp))) continue; if (fu_device_get_parent (dev_tmp) == dev) { GNode *child = g_node_append_data (root, dev_tmp); @@ -414,7 +424,7 @@ g_autoptr(GPtrArray) devs = NULL; /* load engine */ - if (!fu_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* print */ @@ -477,6 +487,56 @@ return g_object_ref (dev); } +static void +fu_util_update_device_changed_cb (FwupdClient *client, + FwupdDevice *device, + FuUtilPrivate *priv) +{ + g_autofree gchar *str = NULL; + + /* allowed to set whenever the device has changed */ + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + /* same as last time, so ignore */ + if (priv->current_device != NULL && + fwupd_device_compare (priv->current_device, device) == 0) + return; + + /* show message in progressbar */ + 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 (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 fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -500,7 +560,7 @@ } /* load engine */ - if (!fu_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* get device */ @@ -514,13 +574,48 @@ return FALSE; } + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect (priv->engine, "device-changed", + 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 (priv->prepare_blob) { + g_autoptr(GPtrArray) devices = NULL; + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_ptr_array_add (devices, g_object_ref (device)); + if (!fu_engine_composite_prepare (priv->engine, devices, error)) { + g_prefix_error (error, "failed to prepare composite action: "); + return FALSE; + } + } + priv->flags = FWUPD_INSTALL_FLAG_NO_HISTORY; + if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error)) + return FALSE; + if (priv->cleanup_blob) { + g_autoptr(FuDevice) device_new = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the possibly new device from the old ID */ + device_new = fu_engine_get_device (priv->engine, + fu_device_get_id (device), + &error_local); + if (device_new == NULL) { + g_debug ("failed to find new device: %s", + error_local->message); + } else { + g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_ptr_array_add (devices_new, g_steal_pointer (&device_new)); + if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) { + g_prefix_error (error, "failed to cleanup composite action: "); + return FALSE; + } + } + } + + fu_util_display_current_message (priv); + + /* success */ + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } static gint @@ -532,17 +627,59 @@ } static gboolean +fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error) +{ + const gchar *argv[][5] = { { "wget", uri, "-o", fn, NULL }, + { "curl", uri, "--output", fn, NULL }, + { NULL } }; + for (guint i = 0; argv[i][0] != NULL; i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_common_find_program_in_path (argv[i][0], &error_local)) { + g_debug ("%s", error_local->message); + continue; + } + return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error); + } + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no supported out-of-process downloaders found"); + return FALSE; +} + +static gchar * +fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) +{ + g_autofree gchar *filename = NULL; + g_autoptr(SoupURI) uri = NULL; + + /* a local file */ + uri = soup_uri_new (perhapsfn); + if (uri == NULL) + return g_strdup (perhapsfn); + + /* download the firmware to a cachedir */ + filename = fu_util_get_user_cache_path (perhapsfn); + if (!fu_common_mkdir_parent (filename, error)) + return NULL; + if (!fu_util_download_out_of_process (perhapsfn, filename, error)) + return NULL; + return g_steal_pointer (&filename); +} + +static gboolean fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) { - GPtrArray *apps; - g_autoptr(AsStore) store = NULL; + g_autofree gchar *filename = 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_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* handle both forms */ @@ -566,22 +703,29 @@ return FALSE; } - /* parse store */ - blob_cab = fu_common_get_contents_bytes (values[0], error); + /* download if required */ + filename = fu_util_download_if_required (priv, values[0], error); + if (filename == NULL) + return FALSE; + + /* parse silo */ + blob_cab = fu_common_get_contents_bytes (filename, error); if (blob_cab == NULL) { - fu_util_maybe_prefix_sandbox_error (values[0], error); + 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++) { @@ -590,18 +734,21 @@ 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)); } @@ -617,21 +764,124 @@ return FALSE; } + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect (priv->engine, "device-changed", + 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; + } + + /* save the device state for other applications to see */ + if (!fu_util_save_current_state (priv, error)) + return FALSE; + /* 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, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + g_signal_connect (priv->engine, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + + 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); + 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; + } + + /* save the device state for other applications to see */ + if (!fu_util_save_current_state (priv, error)) + return FALSE; + + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } static gboolean fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; /* load engine */ - if (!fu_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* invalid args */ @@ -655,6 +905,9 @@ } /* run vfunc */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; return fu_device_detach (device, error); } @@ -662,9 +915,10 @@ fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; /* load engine */ - if (!fu_engine_load (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* invalid args */ @@ -688,9 +942,325 @@ } /* run vfunc */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; return fu_device_attach (device, error); } +static gboolean +fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) +{ + gboolean has_pending = FALSE; + g_autoptr(FuHistory) history = fu_history_new (); + g_autoptr(GPtrArray) devices = NULL; + + /* check the history database before starting the daemon */ + if (g_strv_length (values) == 0) { + devices = fu_history_get_devices (history, error); + if (devices == NULL) + return FALSE; + } else if (g_strv_length (values) == 1) { + FuDevice *device; + device = fu_history_get_device_by_id (history, values[0], error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_ptr_array_add (devices, device); + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* nothing to do */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index (devices, i); + if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + fu_engine_add_plugin_filter (priv->engine, + fu_device_get_plugin (dev)); + has_pending = TRUE; + } + } + if (!has_pending) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No firmware to activate"); + return FALSE; + + } + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error)) + return FALSE; + + /* activate anything with _NEEDS_ACTIVATION */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) + continue; + /* TRANSLATORS: shown when shutting down to switch to the new version */ + g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device)); + if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuSmbios) smbios = fu_smbios_new (); + g_autoptr(FuHwids) hwids = fu_hwids_new (); + const gchar *hwid_keys[] = { + FU_HWIDS_KEY_BIOS_VENDOR, + FU_HWIDS_KEY_BIOS_VERSION, + FU_HWIDS_KEY_BIOS_MAJOR_RELEASE, + FU_HWIDS_KEY_BIOS_MINOR_RELEASE, + FU_HWIDS_KEY_MANUFACTURER, + FU_HWIDS_KEY_FAMILY, + FU_HWIDS_KEY_PRODUCT_NAME, + FU_HWIDS_KEY_PRODUCT_SKU, + FU_HWIDS_KEY_ENCLOSURE_KIND, + FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, + FU_HWIDS_KEY_BASEBOARD_PRODUCT, + NULL }; + + /* read DMI data */ + if (g_strv_length (values) == 0) { + if (!fu_smbios_setup (smbios, error)) + return FALSE; + } else if (g_strv_length (values) == 1) { + if (!fu_smbios_setup_from_file (smbios, values[0], error)) + return FALSE; + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + if (!fu_hwids_setup (hwids, smbios, error)) + return FALSE; + + /* show debug output */ + g_print ("Computer Information\n"); + g_print ("--------------------\n"); + for (guint i = 0; hwid_keys[i] != NULL; i++) { + const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]); + if (tmp == NULL) + continue; + if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 || + g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) { + guint64 val = g_ascii_strtoull (tmp, NULL, 16); + g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val); + } else { + g_print ("%s: %s\n", hwid_keys[i], tmp); + } + } + + /* show GUIDs */ + g_print ("\nHardware IDs\n"); + g_print ("------------\n"); + for (guint i = 0; i < 15; i++) { + const gchar *keys = NULL; + g_autofree gchar *guid = NULL; + g_autofree gchar *key = NULL; + g_autofree gchar *keys_str = NULL; + g_auto(GStrv) keysv = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the GUID */ + key = g_strdup_printf ("HardwareID-%u", i); + keys = fu_hwids_get_replace_keys (hwids, key); + guid = fu_hwids_get_guid (hwids, key, &error_local); + if (guid == NULL) { + g_print ("%s\n", error_local->message); + continue; + } + + /* show what makes up the GUID */ + keysv = g_strsplit (keys, "&", -1); + keys_str = g_strjoinv (" + ", keysv); + g_print ("{%s} <- %s\n", guid, keys_str); + } + + 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 gboolean +fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *sig = NULL; + + /* check args */ + if (g_strv_length (values) != 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: value expected"); + return FALSE; + } + + /* start engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + sig = fu_engine_self_sign (priv->engine, values[0], + FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP | + FU_KEYRING_SIGN_FLAG_ADD_CERT, error); + if (sig == NULL) + return FALSE; + g_print ("%s\n", sig); + return TRUE; +} + +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; +} + +static gboolean +fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) dev = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* get device */ + if (g_strv_length (values) == 1) { + dev = fu_engine_get_device (priv->engine, values[1], error); + if (dev == NULL) + return FALSE; + } else { + dev = fu_util_prompt_for_device (priv, error); + if (dev == NULL) + return FALSE; + } + + /* add checksums */ + if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error)) + return FALSE; + + /* show checksums */ + str = fu_device_to_string (dev); + g_print ("%s\n", str); + return TRUE; +} + +static gboolean +fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* get all devices from the history database */ + devices = fu_engine_get_history (priv->engine, error); + if (devices == NULL) + return FALSE; + + /* show each device */ + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devices, i); + g_autofree gchar *str = fwupd_device_to_string (dev); + g_print ("%s\n", str); + } + + return TRUE; +} + int main (int argc, char *argv[]) { @@ -698,15 +1268,17 @@ gboolean allow_reinstall = FALSE; gboolean force = FALSE; gboolean ret; - gboolean verbose = FALSE; + gboolean version = FALSE; + gboolean interactive = isatty (fileno (stdout)) != 0; g_auto(GStrv) plugin_glob = NULL; g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new (); g_autofree gchar *cmd_descriptions = NULL; const GOptionEntry options[] = { - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + { "version", '\0', 0, G_OPTION_ARG_NONE, &version, /* TRANSLATORS: command line option */ - _("Show extra debugging information"), NULL }, + _("Show client and daemon versions"), NULL }, { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall, /* TRANSLATORS: command line option */ _("Allow re-installing existing firmware versions"), NULL }, @@ -716,12 +1288,24 @@ { "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 }, { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob, /* TRANSLATORS: command line option */ _("Manually whitelist specific plugins"), NULL }, + { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob, + /* TRANSLATORS: command line option */ + _("Run the plugin composite prepare routine when using install-blob"), NULL }, + { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob, + /* TRANSLATORS: command line option */ + _("Run the plugin composite cleanup routine when using install-blob"), NULL }, + { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state, + /* TRANSLATORS: command line option */ + _("Save device state into a JSON file between executions"), NULL }, { NULL} }; @@ -732,7 +1316,7 @@ textdomain (GETTEXT_PACKAGE); /* ensure root user */ - if (getuid () != 0 || geteuid () != 0) + if (interactive && (getuid () != 0 || geteuid () != 0)) /* TRANSLATORS: we're poking around as a power user */ g_printerr ("%s\n", _("This program may only work correctly as root")); @@ -741,67 +1325,121 @@ priv->progressbar = fu_progressbar_new (); /* add commands */ - priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_item_free); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, + "build-firmware", + "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]", + /* TRANSLATORS: command description */ + _("Build firmware using a sandbox"), + fu_util_firmware_builder); + fu_util_cmd_array_add (cmd_array, "smbios-dump", "FILE", /* TRANSLATORS: command description */ _("Dump SMBIOS data from a file"), fu_util_smbios_dump); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-plugins", NULL, /* TRANSLATORS: command description */ _("Get all enabled plugins registered with the system"), fu_util_get_plugins); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-details", NULL, /* TRANSLATORS: command description */ _("Gets details about a firmware file"), fu_util_get_details); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, + "get-history", + NULL, + /* TRANSLATORS: command description */ + _("Show history of firmware updates"), + fu_util_get_history); + fu_util_cmd_array_add (cmd_array, + "get-updates", + NULL, + /* TRANSLATORS: command description */ + _("Gets the list of updates for connected hardware"), + fu_util_get_updates); + fu_util_cmd_array_add (cmd_array, "get-devices", NULL, /* TRANSLATORS: command description */ _("Get all devices that support firmware updates"), fu_util_get_devices); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-topology", NULL, /* TRANSLATORS: command description */ _("Get all devices according to the system topology"), fu_util_get_topology); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "watch", NULL, /* TRANSLATORS: command description */ _("Watch for hardware changes"), fu_util_watch); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "install-blob", "FILENAME DEVICE-ID", /* TRANSLATORS: command description */ _("Install a firmware blob on a device"), fu_util_install_blob); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "install", "FILE [ID]", /* TRANSLATORS: command description */ _("Install a firmware file on this hardware"), fu_util_install); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "attach", "DEVICE-ID", /* TRANSLATORS: command description */ _("Attach to firmware mode"), fu_util_attach); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "detach", "DEVICE-ID", /* TRANSLATORS: command description */ _("Detach to bootloader mode"), fu_util_detach); + fu_util_cmd_array_add (cmd_array, + "activate", + "[DEVICE-ID]", + /* TRANSLATORS: command description */ + _("Activate pending devices"), + fu_util_activate); + fu_util_cmd_array_add (cmd_array, + "hwids", + "[FILE]", + /* TRANSLATORS: command description */ + _("Return all the hardware IDs for the machine"), + fu_util_hwids); + fu_util_cmd_array_add (cmd_array, + "monitor", + NULL, + /* TRANSLATORS: command description */ + _("Monitor the daemon for events"), + fu_util_monitor); + fu_util_cmd_array_add (cmd_array, + "update", + NULL, + /* TRANSLATORS: command description */ + _("Update all devices that match local metadata"), + fu_util_update); + fu_util_cmd_array_add (cmd_array, + "self-sign", + "TEXT", + /* TRANSLATORS: command description */ + C_("command-description", + "Sign data using the client certificate"), + fu_util_self_sign); + fu_util_cmd_array_add (cmd_array, + "verify-update", + "[DEVICE_ID]", + /* TRANSLATORS: command description */ + _("Update the stored metadata with current contents"), + fu_util_verify_update); /* do stuff on ctrl+c */ priv->cancellable = g_cancellable_new (); @@ -812,12 +1450,17 @@ G_CALLBACK (fu_util_cancelled_cb), priv); /* sort by command name */ - g_ptr_array_sort (priv->cmd_array, - (GCompareFunc) fu_sort_command_name_cb); + fu_util_cmd_array_sort (cmd_array); + + /* non-TTY consoles cannot answer questions */ + if (!interactive) { + 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); + cmd_descriptions = fu_util_cmd_array_to_string (cmd_array); g_option_context_set_summary (priv->context, cmd_descriptions); g_option_context_set_description (priv->context, "This tool allows an administrator to use the fwupd plugins " @@ -826,6 +1469,7 @@ /* TRANSLATORS: program name */ g_set_application_name (_("Firmware Utility")); g_option_context_add_main_entries (priv->context, options, NULL); + g_option_context_add_group (priv->context, fu_debug_get_option_group ()); ret = g_option_context_parse (priv->context, &argc, &argv, &error); if (!ret) { /* TRANSLATORS: the user didn't read the man page */ @@ -834,16 +1478,7 @@ return EXIT_FAILURE; } - /* set verbose? */ - if (verbose) { - g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); - } else { - g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, - fu_util_ignore_cb, NULL); - } - /* set flags */ - priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; if (allow_reinstall) priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; if (allow_older) @@ -859,9 +1494,6 @@ g_signal_connect (priv->engine, "device-removed", G_CALLBACK (fu_main_engine_device_removed_cb), priv); - g_signal_connect (priv->engine, "device-changed", - G_CALLBACK (fu_main_engine_device_changed_cb), - priv); g_signal_connect (priv->engine, "status-changed", G_CALLBACK (fu_main_engine_status_changed_cb), priv); @@ -869,12 +1501,19 @@ G_CALLBACK (fu_main_engine_percentage_changed_cb), priv); + /* just show versions and exit */ + if (version) { + g_autofree gchar *version_str = fu_util_get_versions (); + g_print ("%s\n", version_str); + return EXIT_SUCCESS; + } + /* any plugin whitelist specified */ for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++) fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]); /* run the specified command */ - ret = fu_util_run (priv, argv[1], (gchar**) &argv[2], &error); + ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error); if (!ret) { if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { g_autofree gchar *tmp = NULL; diff -Nru fwupd-1.0.9/src/fu-udev-device.c fwupd-1.2.10/src/fu-udev-device.c --- fwupd-1.0.9/src/fu-udev-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-udev-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2017-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuUdevDevice" + +#include "config.h" + +#include + +#include "fu-device-private.h" +#include "fu-udev-device-private.h" + +/** + * SECTION:fu-udev-device + * @short_description: a udev device + * + * An object that represents a udev device. + * + * See also: #FuDevice + */ + +typedef struct +{ + GUdevDevice *udev_device; + guint16 vendor; + guint16 model; + guint8 revision; +} FuUdevDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuUdevDevice, fu_udev_device, FU_TYPE_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 + +enum { + PROP_0, + PROP_UDEV_DEVICE, + PROP_LAST +}; + +enum { + SIGNAL_CHANGED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = { 0 }; + +#define GET_PRIVATE(o) (fu_udev_device_get_instance_private (o)) + +/** + * fu_udev_device_emit_changed: + * @self: A #FuUdevDevice + * + * Emits the ::changed signal for the object. + * + * Since: 1.1.2 + **/ +void +fu_udev_device_emit_changed (FuUdevDevice *self) +{ + g_return_if_fail (FU_IS_UDEV_DEVICE (self)); + g_debug ("FuUdevDevice emit changed"); + g_signal_emit (self, signals[SIGNAL_CHANGED], 0); +} + +static guint64 +fu_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *udev_device, const gchar *name) +{ + return fu_common_strtoull (g_udev_device_get_sysfs_attr (udev_device, name)); +} + +static guint16 +fu_udev_device_read_uint16 (const gchar *str) +{ + gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 }; + memcpy (buf, str, 4); + 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) +{ + FuUdevDeviceClass *klass = FU_UDEV_DEVICE_GET_CLASS (device); + FuUdevDevice *self = FU_UDEV_DEVICE (device); + 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"); + priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "device"); + priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "revision"); + + /* fallback to the parent */ + 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 (udev_parent != NULL && + priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0 && + g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), "hidraw") == 0) { + tmp = g_udev_device_get_property (udev_parent, "HID_ID"); + if (tmp != NULL && strlen (tmp) == 22) { + priv->vendor = fu_udev_device_read_uint16 (tmp + 10); + priv->model = fu_udev_device_read_uint16 (tmp + 18); + } + tmp = g_udev_device_get_property (udev_parent, "HID_NAME"); + if (tmp != NULL) { + g_auto(GStrv) vm = g_strsplit (tmp, " ", 2); + if (g_strv_length (vm) == 2) { + if (fu_device_get_vendor (device) == NULL) + fu_device_set_vendor (device, vm[0]); + if (fu_device_get_name (device) == NULL) + fu_device_set_name (device, vm[1]); + } + } + } + + /* set the version if the revision has been set */ + if (fu_device_get_version (device) == NULL) { + if (priv->revision != 0x00) { + g_autofree gchar *version = g_strdup_printf ("%02x", priv->revision); + fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_PLAIN); + } + } + + /* set model */ + if (fu_device_get_name (device) == NULL) { + 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); + } + + /* set vendor */ + if (fu_device_get_vendor (device) == NULL) { + 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, FWUPD_VERSION_FORMAT_UNKNOWN); + } + + /* set vendor ID */ + subsystem = g_ascii_strup (fu_udev_device_get_subsystem (self), -1); + if (subsystem != NULL && priv->vendor != 0x0000) { + g_autofree gchar *vendor_id = NULL; + vendor_id = g_strdup_printf ("%s:0x%04X", subsystem, (guint) priv->vendor); + fu_device_set_vendor_id (device, vendor_id); + } + + /* add GUIDs in order of priority */ + if (priv->vendor != 0x0000 && priv->model != 0x0000 && priv->revision != 0x00) { + g_autofree gchar *devid = NULL; + devid = g_strdup_printf ("%s\\VEN_%04X&DEV_%04X&REV_%02X", + subsystem, priv->vendor, + priv->model, priv->revision); + fu_device_add_instance_id (device, devid); + } + if (priv->vendor != 0x0000 && priv->model != 0x0000) { + g_autofree gchar *devid = NULL; + devid = g_strdup_printf ("%s\\VEN_%04X&DEV_%04X", + subsystem, priv->vendor, priv->model); + fu_device_add_instance_id (device, devid); + } + if (priv->vendor != 0x0000) { + g_autofree gchar *devid = NULL; + devid = g_strdup_printf ("%s\\VEN_%04X", subsystem, priv->vendor); + fu_device_add_instance_id_full (device, devid, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + } + + /* subclassed */ + if (klass->probe != NULL) { + if (!klass->probe (self, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_udev_device_set_dev (FuUdevDevice *self, GUdevDevice *udev_device) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + + g_return_if_fail (FU_IS_UDEV_DEVICE (self)); + + /* set new device */ + g_set_object (&priv->udev_device, udev_device); + if (priv->udev_device == NULL) + 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) +{ + g_return_if_fail (FU_IS_UDEV_DEVICE (self)); + g_return_if_fail (FU_IS_UDEV_DEVICE (donor)); + fu_udev_device_set_dev (FU_UDEV_DEVICE (self), + fu_udev_device_get_dev (FU_UDEV_DEVICE (donor))); +} + +/** + * fu_udev_device_get_dev: + * @self: A #FuUdevDevice + * + * Gets the #GUdevDevice. + * + * Returns: (transfer none): a #GUdevDevice, or %NULL + * + * Since: 1.1.2 + **/ +GUdevDevice * +fu_udev_device_get_dev (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL); + return priv->udev_device; +} + +/** + * fu_udev_device_get_subsystem: + * @self: A #GUdevDevice + * + * Gets the device subsystem, e.g. "pci". + * + * Returns: a subsystem, or NULL if unset or invalid + * + * Since: 1.1.2 + **/ +const gchar * +fu_udev_device_get_subsystem (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL); + return g_udev_device_get_subsystem (priv->udev_device); +} + +/** + * fu_udev_device_get_sysfs_path: + * @self: A #GUdevDevice + * + * Gets the device sysfs path, e.g. "/sys/devices/pci0000:00/0000:00:14.0". + * + * Returns: a local path, or NULL if unset or invalid + * + * Since: 1.1.2 + **/ +const gchar * +fu_udev_device_get_sysfs_path (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL); + return g_udev_device_get_sysfs_path (priv->udev_device); +} + +/** + * fu_udev_device_get_vendor: + * @self: A #GUdevDevice + * + * Gets the device vendor code. + * + * Returns: a vendor code, or 0 if unset or invalid + * + * Since: 1.1.2 + **/ +guint16 +fu_udev_device_get_vendor (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x0000); + return priv->vendor; +} + +/** + * fu_udev_device_get_model: + * @self: A #GUdevDevice + * + * Gets the device device code. + * + * Returns: a vendor code, or 0 if unset or invalid + * + * Since: 1.1.2 + **/ +guint16 +fu_udev_device_get_model (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x0000); + return priv->model; +} + +/** + * fu_udev_device_get_revision: + * @self: A #GUdevDevice + * + * Gets the device revision. + * + * Returns: a vendor code, or 0 if unset or invalid + * + * Since: 1.1.2 + **/ +guint8 +fu_udev_device_get_revision (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x00); + 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 + * @subsystem: A subsystem string, e.g. `usb` + * @error: A #GError, or %NULL + * + * Sets the physical ID from the device subsystem. Plugins should choose the + * subsystem that is "deepest" in the udev tree, for instance choosing 'usb' + * over 'pci' for a mouse device. + * + * Returns: %TRUE if the physical device was set. + * + * Since: 1.1.2 + **/ +gboolean +fu_udev_device_set_physical_id (FuUdevDevice *self, const gchar *subsystem, GError **error) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + const gchar *tmp; + g_autofree gchar *physical_id = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), FALSE); + g_return_val_if_fail (subsystem != NULL, FALSE); + + /* get the correct device */ + if (g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), subsystem) == 0) { + udev_device = g_object_ref (priv->udev_device); + } else { + 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, only got %s", + subsystem, str->str); + return FALSE; + } + } + if (g_strcmp0 (subsystem, "pci") == 0) { + tmp = g_udev_device_get_property (udev_device, "PCI_SLOT_NAME"); + if (tmp == NULL) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find PCI_SLOT_NAME"); + return FALSE; + } + physical_id = g_strdup_printf ("PCI_SLOT_NAME=%s", tmp); + } 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, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find DEVPATH"); + return FALSE; + } + physical_id = g_strdup_printf ("DEVPATH=%s", tmp); + } else if (g_strcmp0 (subsystem, "hid") == 0) { + tmp = g_udev_device_get_property (udev_device, "HID_PHYS"); + if (tmp == NULL) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find HID_PHYS"); + return FALSE; + } + physical_id = g_strdup_printf ("HID_PHYS=%s", tmp); + } else { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot handle subsystem %s", + subsystem); + return FALSE; + } + + /* success */ + fu_device_set_physical_id (FU_DEVICE (self), physical_id); + return TRUE; +} + +static void +fu_udev_device_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FuUdevDevice *self = FU_UDEV_DEVICE (object); + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_UDEV_DEVICE: + g_value_set_object (value, priv->udev_device); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_udev_device_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FuUdevDevice *self = FU_UDEV_DEVICE (object); + switch (prop_id) { + case PROP_UDEV_DEVICE: + fu_udev_device_set_dev (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_udev_device_finalize (GObject *object) +{ + FuUdevDevice *self = FU_UDEV_DEVICE (object); + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + + if (priv->udev_device != NULL) + g_object_unref (priv->udev_device); + + G_OBJECT_CLASS (fu_udev_device_parent_class)->finalize (object); +} + +static void +fu_udev_device_init (FuUdevDevice *self) +{ +} + +static void +fu_udev_device_class_init (FuUdevDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->finalize = fu_udev_device_finalize; + object_class->get_property = fu_udev_device_get_property; + object_class->set_property = fu_udev_device_set_property; + device_class->probe = fu_udev_device_probe; + device_class->incorporate = fu_udev_device_incorporate; + + signals[SIGNAL_CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + pspec = g_param_spec_object ("udev-device", NULL, NULL, + G_UDEV_TYPE_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_UDEV_DEVICE, pspec); +} + +/** + * fu_udev_device_new: + * @udev_device: A #GUdevDevice + * + * Creates a new #FuUdevDevice. + * + * Returns: (transfer full): a #FuUdevDevice + * + * Since: 1.1.2 + **/ +FuUdevDevice * +fu_udev_device_new (GUdevDevice *udev_device) +{ + FuUdevDevice *self = g_object_new (FU_TYPE_UDEV_DEVICE, + "udev-device", udev_device, + NULL); + return FU_UDEV_DEVICE (self); +} diff -Nru fwupd-1.0.9/src/fu-udev-device.h fwupd-1.2.10/src/fu-udev-device.h --- fwupd-1.0.9/src/fu-udev-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-udev-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_UDEV_DEVICE (fu_udev_device_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuUdevDevice, fu_udev_device, FU, UDEV_DEVICE, FuDevice) + +struct _FuUdevDeviceClass +{ + FuDeviceClass parent_class; + gboolean (*probe) (FuUdevDevice *device, + GError **error); + gpointer __reserved[31]; +}; + +FuUdevDevice *fu_udev_device_new (GUdevDevice *udev_device); +GUdevDevice *fu_udev_device_get_dev (FuUdevDevice *self); +const gchar *fu_udev_device_get_sysfs_path (FuUdevDevice *self); +const gchar *fu_udev_device_get_subsystem (FuUdevDevice *self); +guint16 fu_udev_device_get_vendor (FuUdevDevice *self); +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 -Nru fwupd-1.0.9/src/fu-udev-device-private.h fwupd-1.2.10/src/fu-udev-device-private.h --- fwupd-1.0.9/src/fu-udev-device-private.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-udev-device-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-udev-device.h" + +G_BEGIN_DECLS + +void fu_udev_device_emit_changed (FuUdevDevice *self); + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-usb-device.c fwupd-1.2.10/src/fu-usb-device.c --- fwupd-1.0.9/src/fu-usb-device.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-usb-device.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,15 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2017 Richard Hughes +/* + * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#include "config.h" +#define G_LOG_DOMAIN "FuUsbDevice" -#include +#include "config.h" -#include "fu-usb-device.h" +#include "fu-device-private.h" +#include "fu-usb-device-private.h" /** * SECTION:fu-device @@ -24,7 +24,6 @@ { GUsbDevice *usb_device; FuDeviceLocker *usb_device_locker; - gboolean done_probe; } FuUsbDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE (FuUsbDevice, fu_usb_device, FU_TYPE_DEVICE) @@ -37,65 +36,6 @@ #define GET_PRIVATE(o) (fu_usb_device_get_instance_private (o)) static void -fu_usb_device_apply_quirks (FuUsbDevice *device) -{ - FuQuirks *quirks = fu_device_get_quirks (FU_DEVICE (device)); - GUsbDevice *usb_device = fu_usb_device_get_dev (device); - const gchar *type_name = G_OBJECT_TYPE_NAME (device); - const gchar *tmp; - - /* not set */ - if (quirks == NULL) - return; - - /* type */ - g_debug ("looking for USB quirks for %s type", type_name); - tmp = fu_quirks_lookup_by_usb_device (quirks, type_name, usb_device); - if (tmp != NULL) { - g_debug ("default plugin hints set to: %s", tmp); - fu_device_set_plugin_hints (FU_DEVICE (device), tmp); - } - - /* name */ - g_debug ("looking for USB quirks for %s device", - fu_device_get_platform_id (FU_DEVICE (device))); - tmp = fu_quirks_lookup_by_usb_device (quirks, FU_QUIRKS_USB_NAME, usb_device); - if (tmp != NULL) - fu_device_set_name (FU_DEVICE (device), tmp); - - /* summary */ - tmp = fu_quirks_lookup_by_usb_device (quirks, FU_QUIRKS_USB_SUMMARY, usb_device); - if (tmp != NULL) - fu_device_set_summary (FU_DEVICE (device), tmp); - - /* vendor */ - tmp = fu_quirks_lookup_by_usb_device (quirks, FU_QUIRKS_USB_VENDOR, usb_device); - if (tmp != NULL) - fu_device_set_vendor (FU_DEVICE (device), tmp); - - /* version */ - tmp = fu_quirks_lookup_by_usb_device (quirks, FU_QUIRKS_USB_VERSION, usb_device); - if (tmp != NULL) - fu_device_set_version (FU_DEVICE (device), tmp); - - /* icon */ - tmp = fu_quirks_lookup_by_usb_device (quirks, FU_QUIRKS_USB_ICON, usb_device); - if (tmp != NULL) - fu_device_add_icon (FU_DEVICE (device), tmp); - - /* GUID */ - tmp = fu_quirks_lookup_by_usb_device (quirks, FU_QUIRKS_USB_GUID, usb_device); - if (tmp != NULL) - fu_device_add_guid (FU_DEVICE (device), tmp); -} - -static void -fu_usb_device_notify_quirks_cb (FuUsbDevice *device, GParamSpec *pspec, gpointer user_data) -{ - fu_usb_device_apply_quirks (device); -} - -static void fu_usb_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { @@ -143,26 +83,6 @@ static void fu_usb_device_init (FuUsbDevice *device) { - g_signal_connect (device, "notify::quirks", - G_CALLBACK (fu_usb_device_notify_quirks_cb), NULL); -} - -static void -fu_usb_device_class_init (FuUsbDeviceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - object_class->finalize = fu_usb_device_finalize; - object_class->get_property = fu_usb_device_get_property; - object_class->set_property = fu_usb_device_set_property; - - pspec = g_param_spec_object ("usb-device", NULL, NULL, - G_USB_TYPE_DEVICE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_NAME); - g_object_class_install_property (object_class, PROP_USB_DEVICE, pspec); } /** @@ -183,51 +103,29 @@ return priv->usb_device_locker != NULL; } -/** - * fu_usb_device_open: - * @device: A #FuUsbDevice - * @error: A #GError, or %NULL - * - * Opens a USB device, optionally running a object-specific vfunc. - * - * Returns: %TRUE for success - * - * Since: 1.0.2 - **/ -gboolean -fu_usb_device_open (FuUsbDevice *device, GError **error) +static gboolean +fu_usb_device_open (FuDevice *device, GError **error) { - FuUsbDevicePrivate *priv = GET_PRIVATE (device); + FuUsbDevice *self = FU_USB_DEVICE (device); + FuUsbDevicePrivate *priv = GET_PRIVATE (self); FuUsbDeviceClass *klass = FU_USB_DEVICE_GET_CLASS (device); guint idx; - g_autoptr(AsProfile) profile = as_profile_new (); - g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(FuDeviceLocker) locker = NULL; - g_return_val_if_fail (FU_IS_USB_DEVICE (device), FALSE); + g_return_val_if_fail (FU_IS_USB_DEVICE (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* already open */ if (priv->usb_device_locker != NULL) return TRUE; - /* probe */ - if (!fu_usb_device_probe (device, error)) - return FALSE; - - /* profile */ - ptask = as_profile_start (profile, "added{%04x:%04x}", - g_usb_device_get_vid (priv->usb_device), - g_usb_device_get_pid (priv->usb_device)); - g_assert (ptask != NULL); - /* open */ locker = fu_device_locker_new (priv->usb_device, error); if (locker == NULL) return FALSE; /* get vendor */ - if (fu_device_get_vendor (FU_DEVICE (device)) == NULL) { + if (fu_device_get_vendor (device) == NULL) { idx = g_usb_device_get_manufacturer_index (priv->usb_device); if (idx != 0x00) { g_autofree gchar *tmp = NULL; @@ -235,12 +133,12 @@ idx, error); if (tmp == NULL) return FALSE; - fu_device_set_vendor (FU_DEVICE (device), tmp); + fu_device_set_vendor (device, tmp); } } /* get product */ - if (fu_device_get_name (FU_DEVICE (device)) == NULL) { + if (fu_device_get_name (device) == NULL) { idx = g_usb_device_get_product_index (priv->usb_device); if (idx != 0x00) { g_autofree gchar *tmp = NULL; @@ -248,12 +146,12 @@ idx, error); if (tmp == NULL) return FALSE; - fu_device_set_name (FU_DEVICE (device), tmp); + fu_device_set_name (device, tmp); } } /* get serial number */ - if (fu_device_get_serial (FU_DEVICE (device)) == NULL) { + if (fu_device_get_serial (device) == NULL) { idx = g_usb_device_get_serial_number_index (priv->usb_device); if (idx != 0x00) { g_autofree gchar *tmp = NULL; @@ -261,7 +159,7 @@ idx, error); if (tmp == NULL) return FALSE; - fu_device_set_serial (FU_DEVICE (device), tmp); + fu_device_set_serial (device, tmp); } } @@ -272,7 +170,10 @@ if (idx != 0x00) { g_autofree gchar *tmp = NULL; tmp = g_usb_device_get_string_descriptor (priv->usb_device, idx, NULL); - fu_device_set_version (FU_DEVICE (device), tmp); + /* although guessing is a route to insanity, if the device has + * provided the extra data it's because the BCD type was not + * suitable -- and INTEL_ME is not relevant here */ + fu_device_set_version (device, tmp, fu_common_version_guess_format (tmp)); } /* get GUID from the descriptor if set */ @@ -282,12 +183,12 @@ if (idx != 0x00) { g_autofree gchar *tmp = NULL; tmp = g_usb_device_get_string_descriptor (priv->usb_device, idx, NULL); - fu_device_add_guid (FU_DEVICE (device), tmp); + fu_device_add_guid (device, tmp); } /* subclassed */ if (klass->open != NULL) { - if (!klass->open (device, error)) + if (!klass->open (self, error)) return FALSE; } @@ -296,24 +197,14 @@ return TRUE; } -/** - * fu_usb_device_open: - * @device: A #FuUsbDevice - * @error: A #GError, or %NULL - * - * Closes a USB device, optionally running a object-specific vfunc. - * - * Returns: %TRUE for success - * - * Since: 1.0.2 - **/ -gboolean -fu_usb_device_close (FuUsbDevice *device, GError **error) +static gboolean +fu_usb_device_close (FuDevice *device, GError **error) { - FuUsbDevicePrivate *priv = GET_PRIVATE (device); + FuUsbDevice *self = FU_USB_DEVICE (device); + FuUsbDevicePrivate *priv = GET_PRIVATE (self); FuUsbDeviceClass *klass = FU_USB_DEVICE_GET_CLASS (device); - g_return_val_if_fail (FU_IS_USB_DEVICE (device), FALSE); + g_return_val_if_fail (FU_IS_USB_DEVICE (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* already open */ @@ -322,7 +213,7 @@ /* subclassed */ if (klass->close != NULL) { - if (!klass->close (device, error)) + if (!klass->close (self, error)) return FALSE; } @@ -330,42 +221,143 @@ return TRUE; } -/** - * fu_usb_device_probe: - * @device: A #FuUsbDevice - * @error: A #GError, or %NULL - * - * Probes a USB device, setting parameters on the object that does not need - * the device open or the interface claimed. - * If the device is not compatible then an error should be returned. - * - * Returns: %TRUE for success - * - * Since: 1.0.2 - **/ -gboolean -fu_usb_device_probe (FuUsbDevice *device, GError **error) +static gboolean +fu_usb_device_probe (FuDevice *device, GError **error) { - FuUsbDevicePrivate *priv = GET_PRIVATE (device); + FuUsbDevice *self = FU_USB_DEVICE (device); FuUsbDeviceClass *klass = FU_USB_DEVICE_GET_CLASS (device); + FuUsbDevicePrivate *priv = GET_PRIVATE (self); + guint16 release; + g_autofree gchar *devid0 = NULL; + g_autofree gchar *devid1 = NULL; + g_autofree gchar *devid2 = NULL; + g_autofree gchar *vendor_id = NULL; + g_autoptr(GPtrArray) intfs = NULL; - g_return_val_if_fail (FU_IS_USB_DEVICE (device), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + /* set vendor ID */ + vendor_id = g_strdup_printf ("USB:0x%04X", g_usb_device_get_vid (priv->usb_device)); + fu_device_set_vendor_id (device, vendor_id); - /* already done */ - if (priv->done_probe) - return TRUE; + /* 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 = NULL; + version = fu_common_version_from_uint16 (release, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version (device, version, FWUPD_VERSION_FORMAT_BCD); + } + + /* add GUIDs in order of priority */ + devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&REV_%04X", + g_usb_device_get_vid (priv->usb_device), + g_usb_device_get_pid (priv->usb_device), + release); + fu_device_add_instance_id (device, devid2); + devid1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", + g_usb_device_get_vid (priv->usb_device), + g_usb_device_get_pid (priv->usb_device)); + fu_device_add_instance_id (device, devid1); + devid0 = g_strdup_printf ("USB\\VID_%04X", + g_usb_device_get_vid (priv->usb_device)); + fu_device_add_instance_id_full (device, devid0, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + + /* 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_instance_id_full (device, intid1, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + intid2 = g_strdup_printf ("USB\\CLASS_%02X&SUBCLASS_%02X", + g_usb_interface_get_class (intf), + g_usb_interface_get_subclass (intf)); + fu_device_add_instance_id_full (device, intid2, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + intid3 = g_strdup_printf ("USB\\CLASS_%02X", + g_usb_interface_get_class (intf)); + fu_device_add_instance_id_full (device, intid3, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + } /* subclassed */ if (klass->probe != NULL) { - if (!klass->probe (device, error)) + if (!klass->probe (self, error)) return FALSE; } - priv->done_probe = TRUE; + + /* success */ return TRUE; } /** + * fu_usb_device_get_vid: + * @self: A #FuUsbDevice + * + * Gets the device vendor code. + * + * Returns: integer, or 0x0 if unset or invalid + * + * Since: 1.1.2 + **/ +guint16 +fu_usb_device_get_vid (FuUsbDevice *self) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_USB_DEVICE (self), 0x0000); + if (priv->usb_device == NULL) + return 0x0; + return g_usb_device_get_vid (priv->usb_device); +} + +/** + * fu_usb_device_get_pid: + * @self: A #FuUsbDevice + * + * Gets the device product code. + * + * Returns: integer, or 0x0 if unset or invalid + * + * Since: 1.1.2 + **/ +guint16 +fu_usb_device_get_pid (FuUsbDevice *self) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_USB_DEVICE (self), 0x0000); + if (priv->usb_device == NULL) + return 0x0; + return g_usb_device_get_pid (priv->usb_device); +} + +/** + * fu_usb_device_get_platform_id: + * @self: A #FuUsbDevice + * + * Gets the device platform ID. + * + * Returns: string, or NULL if unset or invalid + * + * Since: 1.1.2 + **/ +const gchar * +fu_usb_device_get_platform_id (FuUsbDevice *self) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_USB_DEVICE (self), NULL); + if (priv->usb_device == NULL) + return NULL; + return g_usb_device_get_platform_id (priv->usb_device); +} + +/** * fu_usb_device_set_dev: * @device: A #FuUsbDevice * @usb_device: A #GUsbDevice, or %NULL @@ -378,15 +370,11 @@ fu_usb_device_set_dev (FuUsbDevice *device, GUsbDevice *usb_device) { FuUsbDevicePrivate *priv = GET_PRIVATE (device); - guint16 release; - g_autofree gchar *devid1 = NULL; - g_autofree gchar *devid2 = NULL; - g_autofree gchar *vendor_id = NULL; g_return_if_fail (FU_IS_USB_DEVICE (device)); /* need to re-probe hardware */ - priv->done_probe = FALSE; + fu_device_probe_invalidate (FU_DEVICE (device)); /* allow replacement */ g_set_object (&priv->usb_device, usb_device); @@ -395,35 +383,9 @@ return; } - /* add both device IDs */ - devid1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", - g_usb_device_get_vid (usb_device), - g_usb_device_get_pid (usb_device)); - fu_device_add_guid (FU_DEVICE (device), devid1); - release = g_usb_device_get_release (usb_device); - devid2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&REV_%04X", - g_usb_device_get_vid (usb_device), - g_usb_device_get_pid (usb_device), - release); - fu_device_add_guid (FU_DEVICE (device), devid2); - - /* set vendor ID */ - vendor_id = g_strdup_printf ("USB:0x%04X", g_usb_device_get_vid (usb_device)); - fu_device_set_vendor_id (FU_DEVICE (device), vendor_id); - - /* set the version if the release has been set */ - if (release != 0x0) { - g_autofree gchar *version = as_utils_version_from_uint16 (release, - AS_VERSION_PARSE_FLAG_USE_BCD); - fu_device_set_version (FU_DEVICE (device), version); - } - - /* set USB platform ID automatically */ - fu_device_set_platform_id (FU_DEVICE (device), + /* set device ID automatically */ + fu_device_set_physical_id (FU_DEVICE (device), g_usb_device_get_platform_id (usb_device)); - - /* set the quirks again */ - fu_usb_device_apply_quirks (device); } /** @@ -444,6 +406,15 @@ return priv->usb_device; } +static void +fu_usb_device_incorporate (FuDevice *self, FuDevice *donor) +{ + g_return_if_fail (FU_IS_USB_DEVICE (self)); + g_return_if_fail (FU_IS_USB_DEVICE (donor)); + fu_usb_device_set_dev (FU_USB_DEVICE (self), + fu_usb_device_get_dev (FU_USB_DEVICE (donor))); +} + /** * fu_usb_device_new: * @usb_device: A #GUsbDevice @@ -454,10 +425,33 @@ * * Since: 1.0.2 **/ -FuDevice * +FuUsbDevice * fu_usb_device_new (GUsbDevice *usb_device) { FuUsbDevice *device = g_object_new (FU_TYPE_USB_DEVICE, NULL); fu_usb_device_set_dev (device, usb_device); - return FU_DEVICE (device); + return FU_USB_DEVICE (device); +} + +static void +fu_usb_device_class_init (FuUsbDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *device_class = FU_DEVICE_CLASS (klass); + GParamSpec *pspec; + + object_class->finalize = fu_usb_device_finalize; + object_class->get_property = fu_usb_device_get_property; + object_class->set_property = fu_usb_device_set_property; + device_class->open = fu_usb_device_open; + device_class->close = fu_usb_device_close; + device_class->probe = fu_usb_device_probe; + device_class->incorporate = fu_usb_device_incorporate; + + pspec = g_param_spec_object ("usb-device", NULL, NULL, + G_USB_TYPE_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_USB_DEVICE, pspec); } diff -Nru fwupd-1.0.9/src/fu-usb-device.h fwupd-1.2.10/src/fu-usb-device.h --- fwupd-1.0.9/src/fu-usb-device.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-usb-device.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,12 +1,10 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_USB_DEVICE_H -#define __FU_USB_DEVICE_H +#pragma once #include #include @@ -40,18 +38,12 @@ gpointer __reserved[28]; }; -FuDevice *fu_usb_device_new (GUsbDevice *usb_device); +FuUsbDevice *fu_usb_device_new (GUsbDevice *usb_device); +guint16 fu_usb_device_get_vid (FuUsbDevice *self); +guint16 fu_usb_device_get_pid (FuUsbDevice *self); GUsbDevice *fu_usb_device_get_dev (FuUsbDevice *device); void fu_usb_device_set_dev (FuUsbDevice *device, GUsbDevice *usb_device); -gboolean fu_usb_device_open (FuUsbDevice *device, - GError **error); -gboolean fu_usb_device_close (FuUsbDevice *device, - GError **error); -gboolean fu_usb_device_probe (FuUsbDevice *device, - GError **error); gboolean fu_usb_device_is_open (FuUsbDevice *device); G_END_DECLS - -#endif /* __FU_USB_DEVICE_H */ diff -Nru fwupd-1.0.9/src/fu-usb-device-private.h fwupd-1.2.10/src/fu-usb-device-private.h --- fwupd-1.0.9/src/fu-usb-device-private.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/fu-usb-device-private.h 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-usb-device.h" + +G_BEGIN_DECLS + +const gchar *fu_usb_device_get_platform_id (FuUsbDevice *self); + +G_END_DECLS diff -Nru fwupd-1.0.9/src/fu-util.c fwupd-1.2.10/src/fu-util.c --- fwupd-1.0.9/src/fu-util.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-util.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,15 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuMain" + #include "config.h" #include -#include +#include #include #include #include @@ -19,27 +20,33 @@ #include #include #include -#include #include -#include "fu-hwids.h" #include "fu-history.h" #include "fu-plugin-private.h" #include "fu-progressbar.h" #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) +#ifdef HAVE_SYSTEMD +#include "fu-systemd.h" +#endif /* custom return code */ #define EXIT_NOTHING_TO_DO 2 -typedef struct { +typedef enum { + FU_UTIL_OPERATION_UNKNOWN, + FU_UTIL_OPERATION_UPDATE, + FU_UTIL_OPERATION_DOWNGRADE, + FU_UTIL_OPERATION_INSTALL, + FU_UTIL_OPERATION_LAST +} FuUtilOperation; + +struct FuUtilPrivate { GCancellable *cancellable; GMainLoop *loop; GOptionContext *context; - GPtrArray *cmd_array; SoupSession *soup_session; FwupdInstallFlags flags; FwupdClient *client; @@ -48,138 +55,76 @@ gboolean no_reboot_check; gboolean no_unreported_check; gboolean assume_yes; + gboolean sign; gboolean show_all_devices; -} FuUtilPrivate; - -typedef gboolean (*FuUtilPrivateCb) (FuUtilPrivate *util, - gchar **values, - GError **error); + /* only valid in update and downgrade */ + FuUtilOperation current_operation; + FwupdDevice *current_device; + gchar *current_message; + FwupdDeviceFlags completion_flags; +}; static gboolean fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error); - -typedef struct { - gchar *name; - gchar *arguments; - gchar *description; - FuUtilPrivateCb callback; -} FuUtilItem; +static gboolean fu_util_download_file (FuUtilPrivate *priv, + SoupURI *uri, + const gchar *fn, + const gchar *checksum_expected, + GError **error); static void -fu_util_item_free (FuUtilItem *item) -{ - g_free (item->name); - g_free (item->arguments); - g_free (item->description); - g_free (item); -} - -/* - * fu_sort_command_name_cb: - */ -static gint -fu_sort_command_name_cb (FuUtilItem **item1, FuUtilItem **item2) +fu_util_client_notify_cb (GObject *object, + GParamSpec *pspec, + FuUtilPrivate *priv) { - return g_strcmp0 ((*item1)->name, (*item2)->name); + fu_progressbar_update (priv->progressbar, + fwupd_client_get_status (priv->client), + fwupd_client_get_percentage (priv->client)); } static void -fu_util_add (GPtrArray *array, - const gchar *name, - const gchar *arguments, - const gchar *description, - FuUtilPrivateCb callback) -{ - g_auto(GStrv) names = NULL; - - g_return_if_fail (name != NULL); - g_return_if_fail (description != NULL); - g_return_if_fail (callback != NULL); - - /* add each one */ - names = g_strsplit (name, ",", -1); - for (guint i = 0; names[i] != NULL; i++) { - FuUtilItem *item = g_new0 (FuUtilItem, 1); - item->name = g_strdup (names[i]); - if (i == 0) { - item->description = g_strdup (description); - } else { - /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */ - item->description = g_strdup_printf (_("Alias to %s"), - names[0]); - } - item->arguments = g_strdup (arguments); - item->callback = callback; - g_ptr_array_add (array, item); - } -} - -static gchar * -fu_util_get_descriptions (GPtrArray *array) -{ - gsize len; - const gsize max_len = 35; - GString *string; +fu_util_update_device_changed_cb (FwupdClient *client, + FwupdDevice *device, + FuUtilPrivate *priv) +{ + g_autofree gchar *str = NULL; + + /* allowed to set whenever the device has changed */ + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + /* same as last time, so ignore */ + if (priv->current_device != NULL && + fwupd_device_compare (priv->current_device, device) == 0) + return; - /* print each command */ - string = g_string_new (""); - for (guint i = 0; i < array->len; i++) { - FuUtilItem *item = g_ptr_array_index (array, i); - g_string_append (string, " "); - g_string_append (string, item->name); - len = strlen (item->name) + 2; - if (item->arguments != NULL) { - g_string_append (string, " "); - g_string_append (string, item->arguments); - len += strlen (item->arguments) + 1; - } - if (len < max_len) { - for (gsize j = len; j < max_len + 1; j++) - g_string_append_c (string, ' '); - g_string_append (string, item->description); - g_string_append_c (string, '\n'); - } else { - g_string_append_c (string, '\n'); - for (gsize j = 0; j < max_len + 1; j++) - g_string_append_c (string, ' '); - g_string_append (string, item->description); - g_string_append_c (string, '\n'); - } + /* show message in progressbar */ + 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_DOWNGRADE) { + /* TRANSLATORS: %1 is a device name */ + str = g_strdup_printf (_("Downgrading %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); - /* remove trailing newline */ - if (string->len > 0) - g_string_set_size (string, string->len - 1); - - return g_string_free (string, FALSE); -} - -static gboolean -fu_util_run (FuUtilPrivate *priv, const gchar *command, gchar **values, GError **error) -{ - /* find command */ - for (guint i = 0; i < priv->cmd_array->len; i++) { - FuUtilItem *item = g_ptr_array_index (priv->cmd_array, i); - if (g_strcmp0 (item->name, command) == 0) - return item->callback (priv, values, error); + 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); } - - /* not found */ - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - /* TRANSLATORS: error message */ - _("Command not found")); - return FALSE; -} - -static void -fu_util_client_notify_cb (GObject *object, - GParamSpec *pspec, - FuUtilPrivate *priv) -{ - fu_progressbar_update (priv->progressbar, - fwupd_client_get_status (priv->client), - fwupd_client_get_percentage (priv->client)); } static FwupdDevice * @@ -243,50 +188,6 @@ } static gboolean -fu_util_setup_networking (FuUtilPrivate *priv, GError **error) -{ - const gchar *http_proxy; - g_autofree gchar *user_agent = NULL; - - /* already done */ - if (priv->soup_session != NULL) - return TRUE; - - /* create the soup session */ - user_agent = fwupd_build_user_agent (PACKAGE_NAME, PACKAGE_VERSION); - priv->soup_session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent, - SOUP_SESSION_TIMEOUT, 60, - NULL); - if (priv->soup_session == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to setup networking"); - return FALSE; - } - - /* set the proxy */ - http_proxy = g_getenv ("https_proxy"); - if (http_proxy == NULL) - http_proxy = g_getenv ("http_proxy"); - if (http_proxy != NULL) { - g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy); - if (proxy_uri == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "invalid proxy URI: %s", http_proxy); - return FALSE; - } - g_object_set (priv->soup_session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL); - } - - /* this disables the double-compression of the firmware.xml.gz file */ - soup_session_remove_feature_by_type (priv->soup_session, SOUP_TYPE_CONTENT_DECODER); - return TRUE; -} - -static gboolean fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error) { g_autoptr(GError) error_local = NULL; @@ -385,6 +286,60 @@ 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) { @@ -395,12 +350,12 @@ 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; /* show and ask user to confirm */ - g_print ("%s", warning_plain); + fu_util_warning_box (warning_plain, 80); if (!priv->assume_yes) { /* ask for permission */ g_print ("\n%s [Y|n]: ", @@ -446,7 +401,8 @@ { for (guint i = 0; i < devs->len; i++) { FwupdDevice *dev_tmp = g_ptr_array_index (devs, i); - if (!(fwupd_device_has_flag (dev_tmp, FWUPD_DEVICE_FLAG_UPDATABLE) || priv->show_all_devices)) + if (!priv->show_all_devices && + !fu_util_is_interesting_device (dev_tmp)) continue; if (fwupd_device_get_parent (dev_tmp) == dev) { GNode *child = g_node_append_data (root, dev_tmp); @@ -500,8 +456,7 @@ g_autofree gchar *tmp = NULL; FwupdDevice *dev = g_ptr_array_index (devs, i); if (!priv->show_all_devices) { - if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE) && - !fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + if (!fu_util_is_interesting_device (dev)) continue; } tmp = fwupd_device_to_string (dev); @@ -515,10 +470,40 @@ return TRUE; } +static gchar * +fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) +{ + g_autofree gchar *filename = NULL; + g_autoptr(SoupURI) uri = NULL; + + /* a local file */ + uri = soup_uri_new (perhapsfn); + if (uri == NULL) + return g_strdup (perhapsfn); + + /* download the firmware to a cachedir */ + filename = fu_util_get_user_cache_path (perhapsfn); + if (!fu_common_mkdir_parent (filename, error)) + return NULL; + if (!fu_util_download_file (priv, uri, filename, NULL, error)) + return NULL; + 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) { const gchar *id; + g_autofree gchar *filename = NULL; /* handle both forms */ if (g_strv_length (values) == 1) { @@ -533,8 +518,28 @@ return FALSE; } + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect (priv->client, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + /* install with flags chosen by the user */ - return fwupd_client_install (priv->client, id, values[0], priv->flags, NULL, error); + filename = fu_util_download_if_required (priv, values[0], error); + if (filename == NULL) + return FALSE; + + 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 @@ -563,160 +568,6 @@ } 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) -{ - gint vercmp; - guint cnt = 0; - g_autofree gchar *link = NULL; - g_autoptr(GPtrArray) results = NULL; - g_autoptr(FuHistory) history = NULL; - - /* verify this is pointing to our cache */ - link = g_file_read_link (FU_OFFLINE_TRIGGER_FILENAME, NULL); - if (link == NULL) { - g_debug ("No %s, exiting", FU_OFFLINE_TRIGGER_FILENAME); - return TRUE; - } - if (g_strcmp0 (link, "/var/lib/fwupd") != 0) { - g_debug ("Another framework set up the trigger, exiting"); - return TRUE; - } - - /* do this first to avoid a loop if this tool segfaults */ - g_unlink (FU_OFFLINE_TRIGGER_FILENAME); - - if (g_strv_length (values) != 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "Invalid arguments: none expected"); - return FALSE; - } - - /* ensure root user */ - if (getuid () != 0 || geteuid () != 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "This function can only be used as root"); - return FALSE; - } - - /* get prepared updates */ - history = fu_history_new (); - results = fu_history_get_devices (history, error); - if (results == NULL) - return FALSE; - - /* apply each update */ - for (guint i = 0; i < results->len; i++) { - FwupdDevice *dev = g_ptr_array_index (results, i); - FwupdRelease *rel = fwupd_device_get_release_default (dev); - - /* check not already done */ - if (fwupd_device_get_update_state (dev) != FWUPD_UPDATE_STATE_PENDING) - continue; - - /* tell the user what's going to happen */ - vercmp = as_utils_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 - * e.g. "1.2.3" */ - g_print (_("Reinstalling %s with %s... "), - fwupd_device_get_name (dev), - fwupd_release_get_version (rel)); - } else if (vercmp > 0) { - /* TRANSLATORS: the first replacement is a display name - * e.g. "ColorHugALS" and the second and third are - * version numbers e.g. "1.2.3" */ - g_print (_("Downgrading %s from %s to %s... "), - fwupd_device_get_name (dev), - fwupd_device_get_version (dev), - fwupd_release_get_version (rel)); - } else if (vercmp < 0) { - /* TRANSLATORS: the first replacement is a display name - * e.g. "ColorHugALS" and the second and third are - * version numbers e.g. "1.2.3" */ - g_print (_("Updating %s from %s to %s... "), - fwupd_device_get_name (dev), - fwupd_device_get_version (dev), - fwupd_release_get_version (rel)); - } - if (!fwupd_client_install (priv->client, - fwupd_device_get_id (dev), - fwupd_release_get_filename (rel), - priv->flags, - NULL, - error)) - return FALSE; - cnt++; - } - - /* nothing to do */ - if (cnt == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "No updates prepared"); - return FALSE; - } - - /* reboot */ - if (!fu_util_update_reboot (error)) - return FALSE; - - g_print ("%s\n", _("Done!")); - return TRUE; -} - -static gboolean fu_util_clear_history (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(FuHistory) history = fu_history_new (); @@ -734,6 +585,7 @@ const gchar *server_msg = NULL; guint status_code; g_autofree gchar *data = NULL; + g_autofree gchar *sig = NULL; g_autoptr(JsonParser) json_parser = NULL; g_autoptr(SoupMessage) msg = NULL; @@ -742,10 +594,21 @@ if (data == NULL) return FALSE; + /* self sign data */ + if (priv->sign) { + sig = fwupd_client_self_sign (priv->client, data, + FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP, + priv->cancellable, error); + if (sig == NULL) + return FALSE; + } + /* ask for permission */ if (!priv->assume_yes) { fu_util_print_data (_("Target"), report_uri); fu_util_print_data (_("Payload"), data); + if (sig != NULL) + fu_util_print_data (_("Signature"), sig); g_print ("%s [Y|n]: ", _("Proceed with upload?")); if (!fu_util_prompt_for_boolean (TRUE)) { g_set_error_literal (error, @@ -757,9 +620,17 @@ } /* POST request */ - msg = soup_message_new (SOUP_METHOD_POST, report_uri); - soup_message_set_request (msg, "application/json; charset=utf-8", - SOUP_MEMORY_COPY, data, strlen (data)); + if (sig != NULL) { + g_autoptr(SoupMultipart) mp = NULL; + mp = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART); + soup_multipart_append_form_string (mp, "payload", data); + soup_multipart_append_form_string (mp, "signature", sig); + msg = soup_form_request_new_from_multipart (report_uri, mp); + } else { + msg = soup_message_new (SOUP_METHOD_POST, report_uri); + soup_message_set_request (msg, "application/json; charset=utf-8", + SOUP_MEMORY_COPY, data, strlen (data)); + } status_code = soup_session_send_message (priv->soup_session, msg); g_debug ("server returned: %s", msg->response_body->data); @@ -856,8 +727,11 @@ g_autoptr(GPtrArray) remotes = NULL; /* set up networking */ - if (!fu_util_setup_networking (priv, error)) - return FALSE; + if (priv->soup_session == NULL) { + priv->soup_session = fu_util_setup_networking (error); + if (priv->soup_session == NULL) + return FALSE; + } /* create a map of RemoteID to RemoteURI */ remotes = fwupd_client_get_remotes (priv->client, NULL, error); @@ -1111,7 +985,7 @@ if (header_size < body_length) return; - /* calulate percentage */ + /* calculate percentage */ percentage = (guint) ((100 * body_length) / header_size); g_debug ("progress: %u%%", percentage); fu_progressbar_update (priv->progressbar, FWUPD_STATUS_DOWNLOADING, percentage); @@ -1139,8 +1013,11 @@ } /* set up networking */ - if (!fu_util_setup_networking (priv, error)) - return FALSE; + if (priv->soup_session == NULL) { + priv->soup_session = fu_util_setup_networking (error); + if (priv->soup_session == NULL) + return FALSE; + } /* download data */ uri_str = soup_uri_to_string (uri, FALSE); @@ -1237,7 +1114,6 @@ g_autofree gchar *basename_id_asc = NULL; g_autofree gchar *basename_id = NULL; g_autofree gchar *basename = NULL; - g_autofree gchar *cache_dir = NULL; g_autofree gchar *filename = NULL; g_autofree gchar *filename_asc = NULL; g_autoptr(SoupURI) uri = NULL; @@ -1248,8 +1124,7 @@ basename_id = g_strdup_printf ("%s-%s", fwupd_remote_get_id (remote), basename); /* download the metadata */ - cache_dir = g_build_filename (g_get_user_cache_dir (), "fwupdmgr", NULL); - filename = g_build_filename (cache_dir, basename_id, NULL); + filename = fu_util_get_user_cache_path (basename_id); if (!fu_common_mkdir_parent (filename, error)) return FALSE; uri = soup_uri_new (fwupd_remote_get_metadata_uri (remote)); @@ -1259,7 +1134,7 @@ /* download the signature */ basename_asc = g_path_get_basename (fwupd_remote_get_filename_cache_sig (remote)); basename_id_asc = g_strdup_printf ("%s-%s", fwupd_remote_get_id (remote), basename_asc); - filename_asc = g_build_filename (cache_dir, basename_id_asc, NULL); + filename_asc = fu_util_get_user_cache_path (basename_id_asc); uri_sig = soup_uri_new (fwupd_remote_get_metadata_uri_sig (remote)); if (!fu_util_download_file (priv, uri_sig, filename_asc, NULL, error)) return FALSE; @@ -1383,6 +1258,7 @@ g_print ("%s:\n", fwupd_device_get_name (dev)); for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index (rels, i); + FwupdReleaseFlags flags = fwupd_release_get_flags (rel); GPtrArray *checksums; const gchar *tmp; @@ -1390,7 +1266,7 @@ fu_util_print_data (_("Version"), fwupd_release_get_version (rel)); /* TRANSLATORS: section header for the release name */ - fu_util_print_data (_("Name"), fwupd_release_get_name (rel)); + fu_util_print_data (_("Name"), fu_util_release_get_name (rel)); /* TRANSLATORS: section header for the release one line summary */ fu_util_print_data (_("Summary"), fwupd_release_get_summary (rel)); @@ -1403,7 +1279,7 @@ 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); } @@ -1416,6 +1292,24 @@ fu_util_print_data (_("Checksum"), checksum_display); } + /* show flags if set */ + if (flags != FWUPD_RELEASE_FLAG_NONE) { + g_autoptr(GString) str = g_string_new (""); + for (guint j = 0; j < 64; j++) { + if ((flags & ((guint64) 1 << j)) == 0) + continue; + g_string_append_printf (str, "%s,", + fwupd_release_flag_to_string ((guint64) 1 << j)); + } + if (str->len == 0) { + g_string_append (str, fwupd_release_flag_to_string (0)); + } else { + g_string_truncate (str, str->len - 1); + } + /* TRANSLATORS: section header for firmware flags */ + fu_util_print_data (_("Flags"), str->str); + } + /* new line between all but last entries */ if (i != rels->len - 1) g_print ("\n"); @@ -1512,7 +1406,15 @@ if (dev == NULL) return FALSE; - return fwupd_client_unlock (priv->client, fwupd_device_get_id (dev), NULL, error); + if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + if (!fwupd_client_unlock (priv->client, fwupd_device_get_id (dev), NULL, error)) + return FALSE; + + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } static gboolean @@ -1568,6 +1470,45 @@ return fu_util_download_metadata (priv, error); } +static gchar * +fu_util_time_to_str (guint64 tmp) +{ + g_return_val_if_fail (tmp != 0, NULL); + + /* seconds */ + if (tmp < 60) { + /* TRANSLATORS: duration in seconds */ + return g_strdup_printf (ngettext ("%u second", "%u seconds", + (gint) tmp), + (guint) tmp); + } + + /* minutes */ + tmp /= 60; + if (tmp < 60) { + /* TRANSLATORS: duration in minutes */ + return g_strdup_printf (ngettext ("%u minute", "%u minutes", + (gint) tmp), + (guint) tmp); + } + + /* hours */ + tmp /= 60; + if (tmp < 60) { + /* TRANSLATORS: duration in minutes */ + return g_strdup_printf (ngettext ("%u hour", "%u hours", + (gint) tmp), + (guint) tmp); + } + + /* days */ + tmp /= 24; + /* TRANSLATORS: duration in days! */ + return g_strdup_printf (ngettext ("%u day", "%u days", + (gint) tmp), + (guint) tmp); +} + static gboolean fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1605,6 +1546,9 @@ g_print (_("%s has firmware updates:"), fwupd_device_get_name (dev)); g_print ("\n"); + /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ + fu_util_print_data (_("Device ID"), fwupd_device_get_id (dev)); + /* TRANSLATORS: a GUID for the hardware */ guids = fwupd_device_get_guids (dev); for (guint j = 0; j < guids->len; j++) { @@ -1615,6 +1559,7 @@ /* 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 */ @@ -1625,7 +1570,7 @@ fwupd_release_get_version (rel)); /* TRANSLATORS: section header for the release name */ - fu_util_print_data (_("Update Name"), fwupd_release_get_name (rel)); + fu_util_print_data (_("Update Name"), fu_util_release_get_name (rel)); /* TRANSLATORS: section header for the release one line summary */ fu_util_print_data (_("Update Summary"), fwupd_release_get_summary (rel)); @@ -1634,6 +1579,15 @@ 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); @@ -1650,9 +1604,7 @@ 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); @@ -1795,101 +1747,6 @@ 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, @@ -1899,7 +1756,6 @@ GPtrArray *checksums; const gchar *remote_id; const gchar *uri_tmp; - g_autofree gchar *basename = NULL; g_autofree gchar *fn = NULL; g_autofree gchar *uri_str = NULL; g_autoptr(SoupURI) uri = NULL; @@ -1916,13 +1772,17 @@ 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); @@ -1939,8 +1799,7 @@ g_print ("Downloading %s for %s...\n", fwupd_release_get_version (rel), fwupd_device_get_name (dev)); - basename = g_path_get_basename (uri_str); - fn = g_build_filename (g_get_user_cache_dir (), "fwupdmgr", basename, NULL); + fn = fu_util_get_user_cache_path (uri_str); if (!fu_common_mkdir_parent (fn, error)) return FALSE; checksums = fwupd_release_get_checksums (rel); @@ -1949,9 +1808,9 @@ fwupd_checksum_get_best (checksums), error)) return FALSE; - g_print ("Updating %s on %s...\n", - fwupd_release_get_version (rel), - fwupd_device_get_name (dev)); + /* if the device specifies ONLY_OFFLINE automatically set this flag */ + if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) + priv->flags |= FWUPD_INSTALL_FLAG_OFFLINE; return fwupd_client_install (priv->client, fwupd_device_get_id (dev), fn, priv->flags, NULL, error); @@ -1960,13 +1819,15 @@ static gboolean fu_util_update_all (FuUtilPrivate *priv, GError **error) { - gboolean requires_reboot = FALSE; g_autoptr(GPtrArray) devices = NULL; /* get devices from daemon */ devices = fwupd_client_get_devices (priv->client, NULL, error); if (devices == NULL) return FALSE; + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + g_signal_connect (priv->client, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); FwupdRelease *rel; @@ -1988,8 +1849,8 @@ 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 */ @@ -1998,18 +1859,7 @@ 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 @@ -2024,6 +1874,11 @@ if (dev == NULL) return FALSE; + /* get devices from daemon */ + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + g_signal_connect (priv->client, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + /* get the releases for this device and filter for validity */ rels = fwupd_client_get_upgrades (priv->client, fwupd_device_get_id (dev), @@ -2034,6 +1889,8 @@ 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"); @@ -2041,17 +1898,7 @@ } /* 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 */ - _("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 @@ -2129,71 +1976,163 @@ rel = fu_util_prompt_for_release (priv, rels, error); if (rel == NULL) return FALSE; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_DOWNGRADE; + g_signal_connect (priv->client, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; return fu_util_update_device_with_release (priv, dev, rel, error); } static gboolean -fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) { - g_autoptr(FuSmbios) smbios = fu_smbios_new (); - g_autoptr(FuHwids) hwids = fu_hwids_new (); - const gchar *hwid_keys[] = { - FU_HWIDS_KEY_BIOS_VENDOR, - FU_HWIDS_KEY_BIOS_VERSION, - FU_HWIDS_KEY_BIOS_MAJOR_RELEASE, - FU_HWIDS_KEY_BIOS_MINOR_RELEASE, - FU_HWIDS_KEY_MANUFACTURER, - FU_HWIDS_KEY_FAMILY, - FU_HWIDS_KEY_PRODUCT_NAME, - FU_HWIDS_KEY_PRODUCT_SKU, - FU_HWIDS_KEY_ENCLOSURE_KIND, - FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, - FU_HWIDS_KEY_BASEBOARD_PRODUCT, - NULL }; - - /* read DMI data */ - if (!fu_smbios_setup (smbios, error)) - return FALSE; - if (!fu_hwids_setup (hwids, smbios, error)) - return FALSE; - - /* show debug output */ - g_print ("Computer Information\n"); - g_print ("--------------------\n"); - for (guint i = 0; hwid_keys[i] != NULL; i++) { - const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]); - if (tmp == NULL) - continue; - g_print ("%s: %s\n", hwid_keys[i], tmp); - } - - /* show GUIDs */ - g_print ("\nHardware IDs\n"); - g_print ("------------\n"); - for (guint i = 0; i < 15; i++) { - const gchar *keys = NULL; - g_autofree gchar *guid = NULL; - g_autofree gchar *key = NULL; - g_autofree gchar *keys_str = NULL; - g_auto(GStrv) keysv = NULL; - g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) devices = NULL; + gboolean has_pending = FALSE; - /* get the GUID */ - key = g_strdup_printf ("HardwareID-%u", i); - keys = fu_hwids_get_replace_keys (hwids, key); - guid = fu_hwids_get_guid (hwids, key, &error_local); - if (guid == NULL) { - g_print ("%s\n", error_local->message); - continue; + /* handle both forms */ + if (g_strv_length (values) == 0) { + /* activate anything with _NEEDS_ACTIVATION */ + devices = fwupd_client_get_devices (priv->client, NULL, error); + if (devices == NULL) + return FALSE; + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + has_pending = TRUE; + break; + } } + } else if (g_strv_length (values) == 1) { + FwupdDevice *device = fwupd_client_get_device_by_id (priv->client, + values[0], + NULL, + error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_ptr_array_add (devices, device); + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) + has_pending = TRUE; + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* nothing to do */ + if (!has_pending) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No firmware to activate"); + return FALSE; + } + + /* activate anything with _NEEDS_ACTIVATION */ + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *device = g_ptr_array_index (devices, i); + if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) + continue; + /* TRANSLATORS: shown when shutting down to switch to the new version */ + g_print ("%s %s…\n", _("Activating firmware update for"), + fwupd_device_get_name (device)); + if (!fwupd_client_activate (priv->client, NULL, + fwupd_device_get_id (device), error)) + return FALSE; + } - /* show what makes up the GUID */ - keysv = g_strsplit (keys, "&", -1); - keys_str = g_strjoinv (" + ", keysv); - g_print ("{%s} <- %s\n", guid, keys_str); + return TRUE; +} + +static gboolean +fu_util_set_approved_firmware (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_auto(GStrv) checksums = NULL; + + /* check args */ + if (g_strv_length (values) != 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: list of checksums expected"); + return FALSE; } + /* call into daemon */ + checksums = g_strsplit (values[0], ",", -1); + return fwupd_client_set_approved_firmware (priv->client, + checksums, + priv->cancellable, + error); +} + +static gboolean +fu_util_get_approved_firmware (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_auto(GStrv) checksums = NULL; + + /* check args */ + if (g_strv_length (values) != 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: none expected"); + return FALSE; + } + + /* call into daemon */ + checksums = fwupd_client_get_approved_firmware (priv->client, + priv->cancellable, + error); + if (checksums == NULL) + return FALSE; + if (g_strv_length (checksums) == 0) { + /* TRANSLATORS: approved firmware has been checked by + * the domain administrator */ + g_print ("%s\n", _("There is no approved firmware.")); + } else { + /* TRANSLATORS: approved firmware has been checked by + * the domain administrator */ + g_print ("%s\n", ngettext ("Approved firmware:", + "Approved firmware:", + g_strv_length (checksums))); + for (guint i = 0; checksums[i] != NULL; i++) + g_print (" * %s\n", checksums[i]); + } + return TRUE; +} + +static gboolean +fu_util_modify_config (FuUtilPrivate *priv, gchar **values, GError **error) +{ + /* check args */ + if (g_strv_length (values) != 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: KEY VALUE expected"); + return FALSE; + } + if (!fwupd_client_modify_config (priv->client, + values[0], values[1], + priv->cancellable, + error)) + return FALSE; + if (!priv->assume_yes) { + g_print ("%s\n [Y|n]: ", + /* TRANSLATORS: configuration changes only take effect on restart */ + _("Restart the daemon to make the change effective?")); + if (!fu_util_prompt_for_boolean (FALSE)) + return TRUE; + } +#ifdef HAVE_SYSTEMD + if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), error)) + return FALSE; +#endif return TRUE; } @@ -2215,12 +2154,13 @@ static void fu_util_private_free (FuUtilPrivate *priv) { - if (priv->cmd_array != NULL) - g_ptr_array_unref (priv->cmd_array); if (priv->client != NULL) g_object_unref (priv->client); + if (priv->current_device != NULL) + 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); @@ -2228,6 +2168,46 @@ g_free (priv); } +static gboolean +fu_util_check_daemon_version (FuUtilPrivate *priv, GError **error) +{ + g_autofree gchar *client = g_strdup_printf ("%i.%i.%i", + FWUPD_MAJOR_VERSION, + FWUPD_MINOR_VERSION, + FWUPD_MICRO_VERSION); + const gchar *daemon = fwupd_client_get_daemon_version (priv->client); + + if (g_strcmp0 (daemon, client) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message */ + _("Unsupported daemon version %s, client version is %s"), + daemon, client); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_util_check_polkit_actions (GError **error) +{ + g_autofree gchar *directory = fu_common_get_path (FU_PATH_KIND_POLKIT_ACTIONS); + g_autofree gchar *filename = g_build_filename (directory, + "org.freedesktop.fwupd.policy", + NULL); + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "PolicyKit files are missing, see https://github.com/hughsie/fwupd/wiki/PolicyKit-files-are-missing"); + return FALSE; + } + + return TRUE; +} + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) @@ -2246,6 +2226,7 @@ gboolean version = FALSE; g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new (); g_autofree gchar *cmd_descriptions = NULL; const GOptionEntry options[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, @@ -2265,10 +2246,13 @@ _("Allow downgrading firmware versions"), NULL }, { "force", '\0', 0, G_OPTION_ARG_NONE, &force, /* TRANSLATORS: command line option */ - _("Override plugin warning"), NULL }, + _("Override warnings and force the action"), NULL }, { "assume-yes", 'y', 0, G_OPTION_ARG_NONE, &priv->assume_yes, /* TRANSLATORS: command line option */ _("Answer yes to all questions"), NULL }, + { "sign", '\0', 0, G_OPTION_ARG_NONE, &priv->sign, + /* TRANSLATORS: command line option */ + _("Sign the uploaded data with the client certificate"), NULL }, { "no-unreported-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_unreported_check, /* TRANSLATORS: command line option */ _("Do not check for unreported history"), NULL }, @@ -2301,163 +2285,162 @@ priv->progressbar = fu_progressbar_new (); /* add commands */ - priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_item_free); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-devices", NULL, /* TRANSLATORS: command description */ _("Get all devices that support firmware updates"), fu_util_get_devices); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-topology", NULL, /* TRANSLATORS: command description */ _("Get all devices according to the system topology"), fu_util_get_topology); - fu_util_add (priv->cmd_array, - "hwids", - NULL, - /* TRANSLATORS: command description */ - _("Return all the hardware IDs for the machine"), - fu_util_hwids); - fu_util_add (priv->cmd_array, - "install-prepared", - NULL, - /* TRANSLATORS: command description */ - _("Install prepared updates now"), - fu_util_install_prepared); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-history", NULL, /* TRANSLATORS: command description */ _("Show history of firmware updates"), fu_util_get_history); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "clear-history", NULL, /* TRANSLATORS: command description */ _("Erase all firmware update history"), fu_util_clear_history); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "report-history", NULL, /* TRANSLATORS: command description */ _("Share firmware history with the developers"), fu_util_report_history); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "install", "FILE [ID]", /* TRANSLATORS: command description */ _("Install a firmware file on this hardware"), fu_util_install); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-details", "FILE", /* TRANSLATORS: command description */ _("Gets details about a firmware file"), fu_util_get_details); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (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, + fu_util_cmd_array_add (cmd_array, "update", NULL, /* TRANSLATORS: command description */ _("Updates all firmware to latest versions available"), fu_util_update); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "verify", "[DEVICE_ID]", /* TRANSLATORS: command description */ _("Gets the cryptographic hash of the dumped firmware"), fu_util_verify); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "unlock", "DEVICE_ID", /* TRANSLATORS: command description */ _("Unlocks the device for firmware access"), fu_util_unlock); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "clear-results", "DEVICE_ID", /* TRANSLATORS: command description */ _("Clears the results from the last update"), fu_util_clear_results); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "clear-offline", NULL, /* TRANSLATORS: command description */ _("Clears any updates scheduled to be updated offline"), fu_util_clear_offline); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-results", "DEVICE_ID", /* TRANSLATORS: command description */ _("Gets the results from the last update"), fu_util_get_results); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-releases", "[DEVICE_ID]", /* TRANSLATORS: command description */ _("Gets the releases for a device"), fu_util_get_releases); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "get-remotes", NULL, /* TRANSLATORS: command description */ _("Gets the configured remotes"), fu_util_get_remotes); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "downgrade", "[DEVICE_ID]", /* TRANSLATORS: command description */ _("Downgrades the firmware on a device"), fu_util_downgrade); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "refresh", "[FILE FILE_SIG REMOTE_ID]", /* TRANSLATORS: command description */ _("Refresh metadata from remote server"), fu_util_refresh); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "verify-update", "[DEVICE_ID]", /* 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, + fu_util_cmd_array_add (cmd_array, "modify-remote", "REMOTE-ID KEY VALUE", /* TRANSLATORS: command description */ _("Modifies a given remote"), fu_util_remote_modify); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "enable-remote", "REMOTE-ID", /* TRANSLATORS: command description */ _("Enables a given remote"), fu_util_remote_enable); - fu_util_add (priv->cmd_array, + fu_util_cmd_array_add (cmd_array, "disable-remote", "REMOTE-ID", /* TRANSLATORS: command description */ _("Disables a given remote"), fu_util_remote_disable); + fu_util_cmd_array_add (cmd_array, + "activate", + "[DEVICE-ID]", + /* TRANSLATORS: command description */ + _("Activate devices"), + fu_util_activate); + fu_util_cmd_array_add (cmd_array, + "get-approved-firmware", + NULL, + /* TRANSLATORS: firmware approved by the admin */ + _("Gets the list of approved firmware."), + fu_util_get_approved_firmware); + fu_util_cmd_array_add (cmd_array, + "set-approved-firmware", + "CHECKSUM1[,CHECKSUM2][,CHECKSUM3]", + /* TRANSLATORS: firmware approved by the admin */ + _("Sets the list of approved firmware."), + fu_util_set_approved_firmware); + fu_util_cmd_array_add (cmd_array, + "modify-config", + "KEY,VALUE", + /* TRANSLATORS: sets something in daemon.conf */ + _("Modifies a daemon configuration value."), + fu_util_modify_config); /* do stuff on ctrl+c */ priv->cancellable = g_cancellable_new (); @@ -2466,19 +2449,19 @@ priv, NULL); /* sort by command name */ - g_ptr_array_sort (priv->cmd_array, - (GCompareFunc) fu_sort_command_name_cb); + fu_util_cmd_array_sort (cmd_array); /* non-TTY consoles cannot answer questions */ if (isatty (fileno (stdout)) == 0) { priv->no_unreported_check = TRUE; priv->no_metadata_check = TRUE; priv->no_reboot_check = TRUE; + fu_progressbar_set_interactive (priv->progressbar, FALSE); } /* get a list of the commands */ priv->context = g_option_context_new (NULL); - cmd_descriptions = fu_util_get_descriptions (priv->cmd_array); + cmd_descriptions = fu_util_cmd_array_to_string (cmd_array); g_option_context_set_summary (priv->context, cmd_descriptions); g_option_context_set_description (priv->context, "This tool allows an administrator to query and control the " @@ -2525,10 +2508,8 @@ /* just show versions and exit */ if (version) { - g_print ("client version:\t%i.%i.%i\n", - FWUPD_MAJOR_VERSION, - FWUPD_MINOR_VERSION, - FWUPD_MICRO_VERSION); + g_autofree gchar *version_str = fu_util_get_versions(); + g_print ("%s\n", version_str); if (!fwupd_client_connect (priv->client, priv->cancellable, &error)) { g_printerr ("Failed to connect to daemon: %s\n", error->message); @@ -2536,31 +2517,43 @@ } g_print ("daemon version:\t%s\n", fwupd_client_get_daemon_version (priv->client)); -#ifdef FWUPD_GIT_DESCRIBE - g_print ("checkout info:\t%s\n", FWUPD_GIT_DESCRIBE); -#endif - g_print ("compile-time dependency versions\n"); - g_print ("\tappstream-glib:\t%d.%d.%d\n", - AS_MAJOR_VERSION, - AS_MINOR_VERSION, - AS_MICRO_VERSION); - g_print ("\tgusb:\t%d.%d.%d\n", - G_USB_MAJOR_VERSION, - G_USB_MINOR_VERSION, - G_USB_MICRO_VERSION); -#ifdef LIBFWUP_LIBRARY_VERSION - g_print ("\tfwupdate:\t%s\n", - LIBFWUP_LIBRARY_VERSION); -#endif -#ifdef EFIVAR_LIBRARY_VERSION - g_print ("\tefivar:\t%s\n", - EFIVAR_LIBRARY_VERSION); -#endif 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"); + } + + /* check that we have at least this version daemon running */ + if (!fu_util_check_daemon_version (priv, &error)) { + g_printerr ("%s\n", error->message); + return EXIT_FAILURE; + } + +#ifdef HAVE_SYSTEMD + /* make sure the correct daemon is in use */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + !fu_util_using_correct_daemon (&error)) { + g_printerr ("%s\n", error->message); + return EXIT_FAILURE; + } +#endif + + /* make sure polkit actions were installed */ + if (!fu_util_check_polkit_actions (&error)) { + g_printerr ("%s\n", error->message); + return EXIT_FAILURE; + } + /* run the specified command */ - ret = fu_util_run (priv, argv[1], (gchar**) &argv[2], &error); + ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error); if (!ret) { if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { g_autofree gchar *tmp = NULL; diff -Nru fwupd-1.0.9/src/fu-util-common.c fwupd-1.2.10/src/fu-util-common.c --- fwupd-1.0.9/src/fu-util-common.c 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-util-common.c 2019-07-15 18:25:54.000000000 +0000 @@ -1,17 +1,70 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include + #include #include +#include #include "fu-util-common.h" #include "fu-device.h" +#ifdef HAVE_SYSTEMD +#include "fu-systemd.h" +#endif + +#define SYSTEMD_FWUPD_UNIT "fwupd.service" +#define SYSTEMD_SNAP_FWUPD_UNIT "snap.fwupd.fwupd.service" + +const gchar * +fu_util_get_systemd_unit (void) +{ + if (g_getenv ("SNAP") != NULL) + return SYSTEMD_SNAP_FWUPD_UNIT; + return SYSTEMD_FWUPD_UNIT; +} + +#ifdef HAVE_SYSTEMD +static const gchar * +fu_util_get_expected_command (const gchar *target) +{ + if (g_strcmp0 (target, SYSTEMD_SNAP_FWUPD_UNIT) == 0) + return "fwupd.fwupdmgr"; + return "fwupdmgr"; +} +#endif + +gboolean +fu_util_using_correct_daemon (GError **error) +{ +#ifdef HAVE_SYSTEMD + g_autofree gchar *default_target = NULL; + g_autoptr(GError) error_local = NULL; + const gchar *target = fu_util_get_systemd_unit (); + + default_target = fu_systemd_get_default_target (&error_local); + if (default_target == NULL) { + g_debug ("Systemd isn't accessible: %s\n", error_local->message); + return TRUE; + } + if (!fu_systemd_unit_check_exists (target, &error_local)) { + g_debug ("wrong target: %s\n", error_local->message); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + /* TRANSLATORS: error message */ + _("Mismatched daemon and client, use %s instead"), + fu_util_get_expected_command (target)); + return FALSE; + } +#endif + return TRUE; +} + void fu_util_print_data (const gchar *title, const gchar *msg) { @@ -118,3 +171,483 @@ g_print ("%s %s\n", str->str, fu_device_get_id (dev)); return FALSE; } + +gboolean +fu_util_is_interesting_device (FwupdDevice *dev) +{ + if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) + return TRUE; + if (fwupd_device_get_update_error (dev) != NULL) + return TRUE; + return FALSE; +} + +gchar * +fu_util_get_user_cache_path (const gchar *fn) +{ + g_autofree gchar *basename = g_path_get_basename (fn); + g_autofree gchar *cachedir_legacy = NULL; + + /* return the legacy path if it exists rather than renaming it to + * prevent problems when using old and new versions of fwupd */ + cachedir_legacy = g_build_filename (g_get_user_cache_dir (), "fwupdmgr", NULL); + if (g_file_test (cachedir_legacy, G_FILE_TEST_IS_DIR)) + return g_build_filename (cachedir_legacy, basename, NULL); + + return g_build_filename (g_get_user_cache_dir (), "fwupd", basename, NULL); +} + +gchar * +fu_util_get_versions (void) +{ + GString *string = g_string_new (""); + + g_string_append_printf (string, + "client version:\t%i.%i.%i\n", + FWUPD_MAJOR_VERSION, + FWUPD_MINOR_VERSION, + FWUPD_MICRO_VERSION); +#ifdef FWUPD_GIT_DESCRIBE + g_string_append_printf (string, + "checkout info:\t%s\n", FWUPD_GIT_DESCRIBE); +#endif + g_string_append_printf (string, + "compile-time dependency versions\n"); + g_string_append_printf (string, + "\tgusb:\t%d.%d.%d\n", + G_USB_MAJOR_VERSION, + G_USB_MINOR_VERSION, + G_USB_MICRO_VERSION); +#ifdef EFIVAR_LIBRARY_VERSION + g_string_append_printf (string, + "\tefivar:\t%s", + EFIVAR_LIBRARY_VERSION); +#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_LOGIND + /* 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; +} + +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_LOGIND + /* 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; +} + +static void +fu_util_cmd_free (FuUtilCmd *item) +{ + g_free (item->name); + g_free (item->arguments); + g_free (item->description); + g_free (item); +} + +GPtrArray * +fu_util_cmd_array_new (void) +{ + return g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_cmd_free); +} + +static gint +fu_util_cmd_sort_cb (FuUtilCmd **item1, FuUtilCmd **item2) +{ + return g_strcmp0 ((*item1)->name, (*item2)->name); +} + +void +fu_util_cmd_array_sort (GPtrArray *array) +{ + g_ptr_array_sort (array, (GCompareFunc) fu_util_cmd_sort_cb); +} + +void +fu_util_cmd_array_add (GPtrArray *array, + const gchar *name, + const gchar *arguments, + const gchar *description, + FuUtilCmdFunc callback) +{ + g_auto(GStrv) names = NULL; + + g_return_if_fail (name != NULL); + g_return_if_fail (description != NULL); + g_return_if_fail (callback != NULL); + + /* add each one */ + names = g_strsplit (name, ",", -1); + for (guint i = 0; names[i] != NULL; i++) { + FuUtilCmd *item = g_new0 (FuUtilCmd, 1); + item->name = g_strdup (names[i]); + if (i == 0) { + item->description = g_strdup (description); + } else { + /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */ + item->description = g_strdup_printf (_("Alias to %s"), + names[0]); + } + item->arguments = g_strdup (arguments); + item->callback = callback; + g_ptr_array_add (array, item); + } +} + +gboolean +fu_util_cmd_array_run (GPtrArray *array, + FuUtilPrivate *priv, + const gchar *command, + gchar **values, + GError **error) +{ + /* find command */ + for (guint i = 0; i < array->len; i++) { + FuUtilCmd *item = g_ptr_array_index (array, i); + if (g_strcmp0 (item->name, command) == 0) + return item->callback (priv, values, error); + } + + /* not found */ + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + /* TRANSLATORS: error message */ + _("Command not found")); + return FALSE; +} + +gchar * +fu_util_cmd_array_to_string (GPtrArray *array) +{ + gsize len; + const gsize max_len = 35; + GString *string; + + /* print each command */ + string = g_string_new (""); + for (guint i = 0; i < array->len; i++) { + FuUtilCmd *item = g_ptr_array_index (array, i); + g_string_append (string, " "); + g_string_append (string, item->name); + len = strlen (item->name) + 2; + if (item->arguments != NULL) { + g_string_append (string, " "); + g_string_append (string, item->arguments); + len += strlen (item->arguments) + 1; + } + if (len < max_len) { + for (gsize j = len; j < max_len + 1; j++) + g_string_append_c (string, ' '); + g_string_append (string, item->description); + g_string_append_c (string, '\n'); + } else { + g_string_append_c (string, '\n'); + for (gsize j = 0; j < max_len + 1; j++) + g_string_append_c (string, ' '); + g_string_append (string, item->description); + g_string_append_c (string, '\n'); + } + } + + /* remove trailing newline */ + if (string->len > 0) + g_string_set_size (string, string->len - 1); + + return g_string_free (string, FALSE); +} + +SoupSession * +fu_util_setup_networking (GError **error) +{ + const gchar *http_proxy; + g_autofree gchar *user_agent = NULL; + g_autoptr(SoupSession) session = NULL; + + /* create the soup session */ + user_agent = fwupd_build_user_agent (PACKAGE_NAME, PACKAGE_VERSION); + session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent, + SOUP_SESSION_TIMEOUT, 60, + NULL); + if (session == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to setup networking"); + return NULL; + } + + /* set the proxy */ + http_proxy = g_getenv ("https_proxy"); + if (http_proxy == NULL) + http_proxy = g_getenv ("HTTPS_PROXY"); + if (http_proxy == NULL) + http_proxy = g_getenv ("http_proxy"); + if (http_proxy == NULL) + http_proxy = g_getenv ("HTTP_PROXY"); + if (http_proxy != NULL && strlen (http_proxy) > 0) { + g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy); + if (proxy_uri == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid proxy URI: %s", http_proxy); + return NULL; + } + g_object_set (session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL); + } + + /* this disables the double-compression of the firmware.xml.gz file */ + soup_session_remove_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER); + return g_steal_pointer (&session); +} + +gchar * +fu_util_release_get_name (FwupdRelease *release) +{ + const gchar *name = fwupd_release_get_name (release); + GPtrArray *cats = fwupd_release_get_categories (release); + + for (guint i = 0; i < cats->len; i++) { + const gchar *cat = g_ptr_array_index (cats, i); + if (g_strcmp0 (cat, "X-Device") == 0) { + /* TRANSLATORS: a specific part of hardware, + * the first %s is the device name, e.g. 'Unifying Receiver` */ + return g_strdup_printf (_("%s Device Update"), name); + } + if (g_strcmp0 (cat, "X-System") == 0) { + /* TRANSLATORS: the entire system, e.g. all internal devices, + * the first %s is the device name, e.g. 'ThinkPad P50` */ + return g_strdup_printf (_("%s System Update"), name); + } + if (g_strcmp0 (cat, "X-EmbeddedController") == 0) { + /* TRANSLATORS: the EC is typically the keyboard controller chip, + * the first %s is the device name, e.g. 'ThinkPad P50` */ + return g_strdup_printf (_("%s Embedded Controller Update"), name); + } + if (g_strcmp0 (cat, "X-ManagementEngine") == 0) { + /* TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, + * the first %s is the device name, e.g. 'ThinkPad P50` */ + return g_strdup_printf (_("%s ME Update"), name); + } + if (g_strcmp0 (cat, "X-CorporateManagementEngine") == 0) { + /* TRANSLATORS: ME stands for Management Engine (with Intel AMT), + * where the first %s is the device name, e.g. 'ThinkPad P50` */ + return g_strdup_printf (_("%s Corporate ME Update"), name); + } + if (g_strcmp0 (cat, "X-ConsumerManagementEngine") == 0) { + /* TRANSLATORS: ME stands for Management Engine, where + * the first %s is the device name, e.g. 'ThinkPad P50` */ + return g_strdup_printf (_("%s Consumer ME Update"), name); + } + if (g_strcmp0 (cat, "X-Controller") == 0) { + /* TRANSLATORS: the controller is a device that has other devices + * plugged into it, for example ThunderBolt, FireWire or USB, + * the first %s is the device name, e.g. 'Intel ThunderBolt` */ + return g_strdup_printf (_("%s Controller Update"), name); + } + } + + /* TRANSLATORS: this is the fallback where we don't know if the release + * is updating the system, the device, or a device class, or something else -- + * the first %s is the device name, e.g. 'ThinkPad P50` */ + return g_strdup_printf (_("%s Update"), name); +} + +static GPtrArray * +fu_util_strsplit_words (const gchar *text, guint line_len) +{ + g_auto(GStrv) tokens = NULL; + g_autoptr(GPtrArray) lines = g_ptr_array_new (); + g_autoptr(GString) curline = g_string_new (NULL); + + /* sanity check */ + if (text == NULL || text[0] == '\0') + return NULL; + if (line_len == 0) + return NULL; + + /* tokenize the string */ + tokens = g_strsplit (text, " ", -1); + for (guint i = 0; tokens[i] != NULL; i++) { + + /* current line plus new token is okay */ + if (curline->len + strlen (tokens[i]) < line_len) { + g_string_append_printf (curline, "%s ", tokens[i]); + continue; + } + + /* too long, so remove space, add newline and dump */ + if (curline->len > 0) + g_string_truncate (curline, curline->len - 1); + g_ptr_array_add (lines, g_strdup (curline->str)); + g_string_truncate (curline, 0); + g_string_append_printf (curline, "%s ", tokens[i]); + } + + /* any incomplete line? */ + if (curline->len > 0) { + g_string_truncate (curline, curline->len - 1); + g_ptr_array_add (lines, g_strdup (curline->str)); + } + return g_steal_pointer (&lines); +} + +static void +fu_util_warning_box_line (const gchar *start, + const gchar *text, + const gchar *end, + const gchar *padding, + guint width) +{ + guint offset = 0; + if (start != NULL) { + offset += g_utf8_strlen (start, -1); + g_print ("%s", start); + } + if (text != NULL) { + offset += g_utf8_strlen (text, -1); + g_print ("%s", text); + } + if (end != NULL) + offset += g_utf8_strlen (end, -1); + for (guint i = offset; i < width; i++) + g_print ("%s", padding); + if (end != NULL) + g_print ("%s\n", end); +} + +void +fu_util_warning_box (const gchar *str, guint width) +{ + g_auto(GStrv) split = g_strsplit (str, "\n", -1); + + /* header */ + fu_util_warning_box_line ("╔", NULL, "╗", "═", width); + + /* body */ + for (guint i = 0; split[i] != NULL; i++) { + g_autoptr(GPtrArray) lines = fu_util_strsplit_words (split[i], width - 4); + if (lines == NULL) + continue; + for (guint j = 0; j < lines->len; j++) { + const gchar *line = g_ptr_array_index (lines, j); + fu_util_warning_box_line ("║ ", line, " ║", " ", width); + } + fu_util_warning_box_line ("║", NULL, "║", " ", width); + } + + /* footer */ + fu_util_warning_box_line ("╚", NULL, "╝", "═", width); +} diff -Nru fwupd-1.0.9/src/fu-util-common.h fwupd-1.2.10/src/fu-util-common.h --- fwupd-1.0.9/src/fu-util-common.h 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/fu-util-common.h 2019-07-15 18:25:54.000000000 +0000 @@ -1,14 +1,30 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * +/* * Copyright (C) 2017-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ -#ifndef __FU_UTIL_COMMON_H__ -#define __FU_UTIL_COMMON_H__ +#pragma once #include +#include +#include + +G_BEGIN_DECLS + +/* this is only valid for tools */ +#define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST+1) + +typedef struct FuUtilPrivate FuUtilPrivate; +typedef gboolean (*FuUtilCmdFunc) (FuUtilPrivate *util, + gchar **values, + GError **error); +typedef struct { + gchar *name; + gchar *arguments; + gchar *description; + FuUtilCmdFunc callback; +} FuUtilCmd; void fu_util_print_data (const gchar *title, const gchar *msg); @@ -16,5 +32,35 @@ gboolean fu_util_prompt_for_boolean (gboolean def); gboolean fu_util_print_device_tree (GNode *n, gpointer data); +gboolean fu_util_is_interesting_device (FwupdDevice *dev); +gchar *fu_util_get_user_cache_path (const gchar *fn); +SoupSession *fu_util_setup_networking (GError **error); + +gchar *fu_util_get_versions (void); + +void fu_util_warning_box (const gchar *str, + guint width); +gboolean fu_util_prompt_complete (FwupdDeviceFlags flags, + gboolean prompt, + GError **error); +gboolean fu_util_update_reboot (GError **error); + +GPtrArray *fu_util_cmd_array_new (void); +void fu_util_cmd_array_add (GPtrArray *array, + const gchar *name, + const gchar *arguments, + const gchar *description, + FuUtilCmdFunc callback); +gchar *fu_util_cmd_array_to_string (GPtrArray *array); +void fu_util_cmd_array_sort (GPtrArray *array); +gboolean fu_util_cmd_array_run (GPtrArray *array, + FuUtilPrivate *priv, + const gchar *command, + gchar **values, + GError **error); +gchar *fu_util_release_get_name (FwupdRelease *release); + +const gchar *fu_util_get_systemd_unit (void); +gboolean fu_util_using_correct_daemon (GError **error); -#endif /* __FU_UTIL_COMMON_H__ */ +G_END_DECLS diff -Nru fwupd-1.0.9/src/meson.build fwupd-1.2.10/src/meson.build --- fwupd-1.0.9/src/meson.build 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/meson.build 2019-07-15 18:25:54.000000000 +0000 @@ -1,5 +1,3 @@ -cargs = ['-DG_LOG_DOMAIN="Fu"'] - if get_option('daemon') install_data(['org.freedesktop.fwupd.xml'], install_dir : join_paths(datadir, 'dbus-1', 'interfaces') @@ -9,6 +7,7 @@ keyring_deps = [] keyring_src = [] test_deps = [] +init_src = [] if get_option('gpg') keyring_src += 'fu-keyring-gpg.c' @@ -24,19 +23,30 @@ endif endif +if get_option('systemd') + init_src += 'fu-systemd.c' +endif + libfwupdprivate = static_library( 'fwupdprivate', sources : [ + init_src, + 'fu-archive.c', 'fu-common.c', + 'fu-common-guid.c', + 'fu-common-version.c', + 'fu-chunk.c', 'fu-device.c', 'fu-device-locker.c', 'fu-hwids.c', 'fu-history.c', + 'fu-io-channel.c', 'fu-plugin.c', 'fu-progressbar.c', 'fu-quirks.c', 'fu-smbios.c', 'fu-test.c', + 'fu-udev-device.c', 'fu-usb-device.c', ], include_directories : [ @@ -44,18 +54,20 @@ include_directories('../libfwupd'), ], dependencies : [ - appstream_glib, giounix, gudev, gusb, soup, sqlite, libarchive, + libjsonglib, + libxmlb, valgrind, ], + link_with : [ + fwupd, + ], c_args : [ - cargs, - '-DSYSFSFIRMWAREDIR="/sys/firmware"', '-DFU_OFFLINE_DESTDIR=""', ], ) @@ -72,7 +84,7 @@ include_directories('../libfwupd'), ], dependencies : [ - appstream_glib, + libxmlb, giounix, gudev, gusb, @@ -86,7 +98,6 @@ libfwupdprivate, ], c_args : [ - cargs, '-DFU_OFFLINE_DESTDIR=""', ], install : true, @@ -94,6 +105,63 @@ ) endif +if get_option('agent') +fwupdagent = executable( + 'fwupdagent', + sources : [ + 'fu-agent.c', + 'fu-util-common.c', + ], + include_directories : [ + include_directories('..'), + include_directories('../libfwupd'), + ], + dependencies : [ + libxmlb, + giounix, + gudev, + gusb, + soup, + libjsonglib, + ], + link_with : [ + fwupd, + libfwupdprivate, + ], + install : true, + install_dir : join_paths(libexecdir, 'fwupd') +) +endif + +if get_option('systemd') +fwupdagent = executable( + 'fwupdoffline', + sources : [ + 'fu-offline.c', + 'fu-util-common.c', + ], + include_directories : [ + include_directories('..'), + include_directories('../libfwupd'), + ], + dependencies : [ + giounix, + gudev, + gusb, + soup, + ], + link_with : [ + fwupd, + libfwupdprivate, + ], + c_args : [ + '-DFU_OFFLINE_DESTDIR=""', + ], + install : true, + install_dir : join_paths(libexecdir, 'fwupd') +) +endif + resources_src = gnome.compile_resources( 'fwupd-resources', 'fwupd.gresource.xml', @@ -101,14 +169,29 @@ 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, + init_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', @@ -118,7 +201,9 @@ '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', @@ -126,6 +211,7 @@ 'fu-plugin-list.c', 'fu-quirks.c', 'fu-smbios.c', + 'fu-udev-device.c', 'fu-usb-device.c', 'fu-util-common.c', ], @@ -135,7 +221,7 @@ ], dependencies : [ keyring_deps, - appstream_glib, + libxmlb, libgcab, giounix, gmodule, @@ -145,14 +231,13 @@ sqlite, valgrind, libarchive, + libjsonglib, ], link_with : [ fwupd, libfwupdprivate, ], c_args : [ - cargs, - '-DSYSFSFIRMWAREDIR="/sys/firmware"', '-DFU_OFFLINE_DESTDIR=""', ], install : true, @@ -181,10 +266,16 @@ executable( 'fwupd', resources_src, + fu_hash, sources : [ keyring_src, + init_src, + 'fu-archive.c', + 'fu-chunk.c', 'fu-common.c', 'fu-common-cab.c', + 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-keyring.c', 'fu-keyring-result.c', @@ -195,6 +286,8 @@ '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', @@ -203,6 +296,7 @@ 'fu-plugin-list.c', 'fu-quirks.c', 'fu-smbios.c', + 'fu-udev-device.c', 'fu-usb-device.c', ], include_directories : [ @@ -211,7 +305,7 @@ ], dependencies : [ keyring_deps, - appstream_glib, + libxmlb, libgcab, giounix, gmodule, @@ -222,16 +316,16 @@ sqlite, valgrind, libarchive, + libjsonglib, ], link_with : fwupd, c_args : [ - cargs, - '-DSYSFSFIRMWAREDIR="/sys/firmware"', '-DFU_OFFLINE_DESTDIR=""', ], install : true, install_dir : join_paths(libexecdir, 'fwupd') ) + endif if get_option('tests') @@ -246,11 +340,17 @@ hwid_test_firmware, noreqs_test_firmware, test_deps, + fu_hash, sources : [ keyring_src, + init_src, 'fu-self-test.c', + 'fu-archive.c', + 'fu-chunk.c', 'fu-common.c', 'fu-common-cab.c', + 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-engine.c', 'fu-keyring.c', @@ -260,7 +360,9 @@ '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-plugin.c', @@ -269,6 +371,7 @@ 'fu-quirks.c', 'fu-smbios.c', 'fu-test.c', + 'fu-udev-device.c', 'fu-usb-device.c', ], include_directories : [ @@ -277,7 +380,7 @@ ], dependencies : [ keyring_deps, - appstream_glib, + libxmlb, libgcab, giounix, gmodule, @@ -287,41 +390,46 @@ sqlite, valgrind, libarchive, + libjsonglib, ], link_with : [ fwupd, ], c_args : [ - cargs, '-DTESTDATADIR_SRC="' + testdatadir_src + '"', '-DTESTDATADIR_DST="' + testdatadir_dst + '"', '-DTESTDATADIR="' + testdatadir_src + ':' + testdatadir_dst + '"', '-DPLUGINBUILDDIR="' + pluginbuilddir + '"', '-DFU_OFFLINE_DESTDIR="/tmp/fwupd-self-test"', - '-DSYSFSFIRMWAREDIR="' + testdatadir_src + '"', ], ) - test('fu-self-test', e, is_parallel:false, - env: ['FWUPD_DATADIR=' + testdatadir_src, - 'FWUPD_SYSCONFDIR=' + testdatadir_src, - 'FWUPD_PLUGINDIR=' + testdatadir_src, - 'FWUPD_LOCALSTATEDIR=/tmp/fwupd-self-test/var']) + test('fu-self-test', e, is_parallel:false, timeout:180) endif 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', 'fu-quirks.h', + 'fu-udev-device.c', 'fu-usb-device.c', ], nsversion : '1.0', @@ -334,7 +442,7 @@ include_directories('../libfwupd'), ], dependencies : [ - appstream_glib, + libxmlb, gir_dep, giounix, gusb, diff -Nru fwupd-1.0.9/src/org.freedesktop.fwupd.xml fwupd-1.2.10/src/org.freedesktop.fwupd.xml --- fwupd-1.0.9/src/org.freedesktop.fwupd.xml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/src/org.freedesktop.fwupd.xml 2019-07-15 18:25:54.000000000 +0000 @@ -6,7 +6,7 @@ - The interface used for quering firmware for the system. + The interface used for querying firmware for the system. @@ -23,6 +23,17 @@ + + + + + If the daemon has been tainted with a 3rd party plugin. + + + + + + @@ -297,6 +308,25 @@ + + + + + Activate a firmware update on the device. + + + + + + + + An ID, typically the sha hash of the device string. + + + + + + @@ -342,6 +372,44 @@ + + + + + Gets the list of approved firmware that can be applied to devices. + In an enterprise this will be configured by a domain administrator. + + + + + + + The checksums of the archives + + + + + + + + + + + Sets the list of approved firmware that can be applied to devices. + In an enterprise this will be configured by a domain administrator. + + + + + + + The checksums of the archives + + + + + + @@ -400,6 +468,35 @@ + + + + + Modify persistent configuration for daemon + + + + + + + + The key, e.g. 'BlacklistPlugins'. + + + + + + + + + The value of the correct type, e.g. a URL. + + + + + + + @@ -472,6 +569,45 @@ + + + + + + + + + Signs some text, typically using a self-signed PKCS-7 certificate. + + + + + + + + String input data, certainly *NOT* binary data. + + + + + + + + + Options to be used when signing, e.g. + add-cert=True or add-timestamp=True. + + + + + + + + + The detached signature string. + + + diff -Nru fwupd-1.0.9/src/README.md fwupd-1.2.10/src/README.md --- fwupd-1.0.9/src/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/src/README.md 2019-07-15 18:25:54.000000000 +0000 @@ -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 -Nru fwupd-1.0.9/subprojects/flashrom.wrap fwupd-1.2.10/subprojects/flashrom.wrap --- fwupd-1.0.9/subprojects/flashrom.wrap 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/subprojects/flashrom.wrap 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,4 @@ +[wrap-git] +directory = flashrom +url = https://github.com/hughsie/flashrom.git +revision = wip/hughsie/fwupd diff -Nru fwupd-1.0.9/subprojects/.gitignore fwupd-1.2.10/subprojects/.gitignore --- fwupd-1.0.9/subprojects/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/subprojects/.gitignore 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,2 @@ +flashrom +libxmlb diff -Nru fwupd-1.0.9/subprojects/libxmlb.wrap fwupd-1.2.10/subprojects/libxmlb.wrap --- fwupd-1.0.9/subprojects/libxmlb.wrap 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-1.2.10/subprojects/libxmlb.wrap 2019-07-15 18:25:54.000000000 +0000 @@ -0,0 +1,4 @@ +[wrap-git] +directory = libxmlb +url = https://github.com/hughsie/libxmlb.git +revision = 0.1.7 diff -Nru fwupd-1.0.9/.travis.yml fwupd-1.2.10/.travis.yml --- fwupd-1.0.9/.travis.yml 2018-09-11 18:07:39.000000000 +0000 +++ fwupd-1.2.10/.travis.yml 2019-07-15 18:25:54.000000000 +0000 @@ -6,14 +6,11 @@ - docker env: - - OS=debian-s390x - OS=fedora - OS=debian-x86_64 - OS=arch - OS=debian-i386 - OS=ubuntu-x86_64 - - OS=centos - - OS=flatpak install: - ./contrib/ci/generate_docker.py